<!DOCTYPE html>
<html>
  <!-- meta/link... -->
  



<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <!-- Global site tag (gtag.js) - Google Analytics -->


  <title>在Docker中使用MySQL高可用之MHA | 张不胖</title>

  <link rel="icon" type="image/x-icon, image/vnd.microsoft.icon" href="/favicon.ico">
  <link rel="stylesheet" href="https://at.alicdn.com/t/font_1911880_c1nvbyezg17.css">
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">

  
    <script>
        var themeModelId = '';
        if (themeModelId) {
            localStorage.setItem('modelId', themeModelId);
        }
    </script>
    
    <script src="https://cdn.jsdelivr.net/gh/yuang01/live2d-widget@latest/autoload.js"></script>
    <script>
        var live2dOpen = eval('true') || false;
        if (!live2dOpen) {
            localStorage.setItem('waifu-display', 1609323474481);
        }
    </script>
  
  
  
<link rel="stylesheet" href="/css/animate.min.css">

  
<link rel="stylesheet" href="/css/style.css">

  
  
    
<link rel="stylesheet" href="/js/fancybox/jquery.fancybox.min.css">

  
  
    
<link rel="stylesheet" href="/js/shareJs/share.min.css">

  
  <style>
        @media (max-width: 992px) {
            #waifu {
                display: none;
            }
        }
    </style>
    <script src="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
    <link href="//cdn.bootcss.com/pace/1.0.2/themes/pink/pace-theme-flash.css" rel="stylesheet">

    

    <!-- import link -->
    
        
            
        
            
        
    
    <!-- import script -->
    
        
            <script src="/js/calendar.js"></script>
        
            <script src="/js/languages.js"></script>
        
            
        
            
        
    

<meta name="generator" content="Hexo 6.3.0"><link rel="alternate" href="/atom.xml" title="张不胖" type="application/atom+xml">
</head>

  
  <!-- 依赖于jquery和vue -->
  <script src="/js/jquery3.5.1.js"></script>
  <script src="/js/vue2.6.11.js"></script>
  
  <body>
    <!-- 预加载动画 -->
    <!-- 页面预加载动画 -->

    
    <!-- 判断是否为暗黑风格 -->
    <!-- 判断是否为黑夜模式 -->
<script>
  let isDark = JSON.parse(localStorage.getItem('dark')) || JSON.parse('false');

  if (isDark) {
    $(document.body).addClass('darkModel');
  }
</script>

    <!-- 头部导航等 -->
    
<header class="header " id="navHeader"
  style="position: fixed;
  left: 0; top: 0; z-index: 10;width: 100%;"
>
  <div class="header-content">
    <div class="bars">
      <div id="appDrawer" class="sidebar-image">
  <div class="drawer-box-icon">
    <i class="fa fa-bars" aria-hidden="true" @click="showDialogDrawer"></i>
  </div>
  
  <transition name="fade">
    <div class="drawer-box_mask" v-cloak style="display: none;" v-show="visible" @click.self="cancelDialogDrawer">
    </div>
  </transition>
  <div class="drawer-box" :class="{'active': visible}">
    <div class="drawer-box-head bg-color">
      <img class="drawer-box-head_logo" src="/medias/logo.png" alt="logo">
      <h3 class="drawer-box-head_title">张不胖</h3>
      <h5 class="drawer-box-head_desc"></h5>
    </div>
    
    <div class="drawer-box-content">
      <ul class="drawer-box-content_menu">
        
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/" class="drawer-menu-item-link">
                  
                    <i class="fa fa-home" aria-hidden="true"></i>
                  
                  <span class="name">首页</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/musics" class="drawer-menu-item-link">
                  
                    <i class="fa fa-music" aria-hidden="true"></i>
                  
                  <span class="name">音乐</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/archives" class="drawer-menu-item-link">
                  
                    <i class="fa fa-archive" aria-hidden="true"></i>
                  
                  <span class="name">归档</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/tags" class="drawer-menu-item-link">
                  
                    <i class="fa fa-tags" aria-hidden="true"></i>
                  
                  <span class="name">标签</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/categories" class="drawer-menu-item-link">
                  
                    <i class="fa fa-bookmark" aria-hidden="true"></i>
                  
                  <span class="name">分类</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/about" class="drawer-menu-item-link">
                  
                    <i class="fa fa-user" aria-hidden="true"></i>
                  
                  <span class="name">关于</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/friends" class="drawer-menu-item-link">
                  
                  <span class="name">友情链接</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="javascript:;" class="drawer-menu-item-link has-children" @click="openOrCloseMenu(7)">
                  <span>
                    
                      <i class="fa fa-link"></i>
                    
                    <span class="name">更多</span>
                  </span>
                  <i class="fa fa-chevron-left arrow " :class="{'icon-rotate': isOpen(7)}" aria-hidden="true"></i>
                </a>
                <ul class="drawer-sub-menu" v-if="isOpen(7)">
                  
                  <li>
                    <a href="/gallery">
                      
                      <i class="fa fa-music" style="margin-top: -20px;"></i>
                      
                      <span>图库</span>
                    </a>
                  </li>
                  
                  <li>
                    <a target="_blank" rel="noopener" href="http://baidu.com">
                      
                      <i class="fa fa-film" style="margin-top: -20px;"></i>
                      
                      <span>百度</span>
                    </a>
                  </li>
                  
                </ul>
              
            </li>
          
        
        
          <li class="drawer-box-content_item">
            <a target="_blank" rel="noopener" href="https://github.com/zhangcun1113/hexo-theme-bamboo">
              <i class="fa fa-github" aria-hidden="true"></i>
              <span>Github</span>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</div>

<script>
  var body = document.body || document.documentElement || window;
  var vm = new Vue({
    el: '#appDrawer',
    data: {
      visible: false,
      top: 0,
      openArr: [],
    },
    computed: {
    },
    mounted() {
    },
    methods: {
      isOpen(index) {
        if (this.openArr.includes(index)) {
          return true;
        } else {
          return false;
        }
      },
      openOrCloseMenu(curIndex) {
        const index = this.openArr.indexOf(curIndex);
        if (index !== -1) {
          this.openArr.splice(index, 1);
        } else {
          this.openArr.push(curIndex);
        }
      },
      showDialogDrawer() {
        this.visible = true;
        // 防止页面滚动，只能让弹框滚动
        this.top = $(document).scrollTop()
        body.style.cssText = 'width: 100%; height: 100%;overflow: hidden;';
      },
      cancelDialogDrawer() {
        this.visible = false;
        body.removeAttribute('style');
        $(document).scrollTop(this.top)
      }
    },
    created() {}
  })
</script>

    </div>
    <div class="blog-title" id="author-avatar">
      
        <div class="avatar">
          <img src="/medias/logo.png" alt="logo">
        </div>
      
      <a href="/" class="logo">张不胖</a>
    </div>
    <nav class="navbar">
      <ul class="menu">
        
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/" class="menu-item-link" title="首页">
                  
                    <i class="fa fa-home" aria-hidden="true"></i>
                  
                  <span class="name">首页</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/musics" class="menu-item-link" title="音乐">
                  
                    <i class="fa fa-music" aria-hidden="true"></i>
                  
                  <span class="name">音乐</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/archives" class="menu-item-link" title="归档">
                  
                    <i class="fa fa-archive" aria-hidden="true"></i>
                  
                  <span class="name">归档</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/tags" class="menu-item-link" title="标签">
                  
                    <i class="fa fa-tags" aria-hidden="true"></i>
                  
                  <span class="name">标签</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/categories" class="menu-item-link" title="分类">
                  
                    <i class="fa fa-bookmark" aria-hidden="true"></i>
                  
                  <span class="name">分类</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/about" class="menu-item-link" title="关于">
                  
                    <i class="fa fa-user" aria-hidden="true"></i>
                  
                  <span class="name">关于</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/friends" class="menu-item-link" title="友情链接">
                  
                  <span class="name">友情链接</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="javascript:;" class="menu-item-link" title="更多">
                  
                    <i class="fa fa-link"></i>
                  
                  <span class="name">更多</span>
                  <i class="fa fa-chevron-down arrow" aria-hidden="true"></i>
                </a>
                <ul class="sub-menu">
                  
                  <li>
                    <a href="/gallery">
                      
                      <i class="fa fa-music" style="margin-top: -20px;"></i>
                      
                      <span>图库</span>
                    </a>
                  </li>
                  
                  <li>
                    <a target="_blank" rel="noopener" href="http://baidu.com">
                      
                      <i class="fa fa-film" style="margin-top: -20px;"></i>
                      
                      <span>百度</span>
                    </a>
                  </li>
                  
                </ul>
              
            </li>
          
        
      </ul>
      
      
        <div id="appSearch">
  <div class="search"  @click="showDialog()"><i class="fa fa-search" aria-hidden="true"></i></div>
  <transition name="fade">
    <div class="message-box_wrapper" style="display: none;" v-cloak v-show="dialogVisible" @click.self="cancelDialogVisible()">
      <div class="message-box animated bounceInDown">
        <!-- <div class="close" style="overflow: hidden;">
          <i class="fa fa-close" aria-hidden="true" style="float:right;" @click.self="cancelDialogVisible()"></i>
        </div> -->
        <h2>
          <span>
            <i class="fa fa-search" aria-hidden="true"></i>
            <span class="title">本地搜索</span>
          </span>
          <i class="fa fa-close close" pointer style="float:right;" aria-hidden="true" @click.self="cancelDialogVisible()"></i>
        </h2>
        <form class="site-search-form">
          <input type="text"
            placeholder="请输入关键字"
            id="local-search-input" 
            @click="getSearchFile()"
            class="st-search-input"
            v-model="searchInput"
          />
        </form>
        <div class="result-wrapper">
          <div id="local-search-result" class="local-search-result-cls"></div>
        </div>
      </div>
    </div>
  </transition>
</div>
<script src="/js/local_search.js"></script>
<script>
  var body = document.body || document.documentElement || window;
  var vm = new Vue({
    el: '#appSearch',
    data: {
      dialogVisible: false,
      searchInput: '',
      top: 0,
    },
    computed: {
    },
    mounted() {
    },
    methods: {
      showDialog() {
        this.dialogVisible = true;
        // 防止页面滚动，只能让弹框滚动
        this.top = $(document).scrollTop()
        body.style.cssText = 'overflow: hidden;';
      },
      getSearchFile() {
        if (!this.searchInput) {
          getSearchFile("/search.xml");
        }
      },
      cancelDialogVisible() {
        this.dialogVisible = false;
        body.removeAttribute('style');
        $(document).scrollTop(this.top)
      },
    },
    created() {}
  })
</script>
<!-- 解决刷新页面闪烁问题，可以在元素上添加display: none, 或者用vue.extend方法，详情：https://blog.csdn.net/qq_31393401/article/details/81017912 -->
<!-- 下面是搜索基本写法 -->
<!-- <script type="text/javascript" id="local.search.active">
  var inputArea = document.querySelector("#local-search-input");
  inputArea.onclick   = function(){ getSearchFile(); this.onclick = null }
  inputArea.onkeydown = function(){ if(event.keyCode == 13) return false }
</script> -->

      

    </nav>
  </div>
  
    <a target="_blank" rel="noopener" href="https://github.com/zhangcun1113/hexo-theme-bamboo" class="github-corner color-primary" aria-label="View source on GitHub"><svg width="60" height="60" viewBox="0 0 250 250" style="fill:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
  
  
    <div id="he-plugin-simple"></div>
    <script>
      WIDGET = {
        CONFIG: {
          "modules": "012",
          "background": 5,
          "tmpColor": "4A4A4A",
          "tmpSize": 16,
          "cityColor": "4A4A4A",
          "citySize": 16,
          "aqiSize": 16,
          "weatherIconSize": 24,
          "alertIconSize": 18,
          "padding": "10px 10px 10px 10px",
          "shadow": "1",
          "language": "auto",
          "borderRadius": 5,
          "fixed": "false",
          "vertical": "middle",
          "horizontal": "center",
          "key": "2784dd3fcb1e4f0f9a9b579bf69641f2"
        }
      }
    </script>
    <script src="https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0"></script> 
    

    <!-- 当头部导航设置为背景透明的时候  -->
    
      <script src="/js/header/index.js" data-pjax></script>
    
</header>

    <!-- 需要在上面加载的js -->
    <script>
    function loadScript(src, cb) {
        setTimeout(function () {
            var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
            var script = document.createElement('script');
            script.setAttribute('type', 'text/javascript');
            if (cb) script.onload = cb;
            script.setAttribute('src', src);
            HEAD.appendChild(script);
        });
    }

    //https://github.com/filamentgroup/loadCSS
    var loadCSS = function (href, before, media, attributes) {
        var doc = window.document;
        var ss = doc.createElement("link");
        var ref;
        if (before) {
            ref = before;
        }
        else {
            var refs = (doc.body || doc.getElementsByTagName("head")[0]).childNodes;
            ref = refs[refs.length - 1];
        }
        var sheets = doc.styleSheets;
        if (attributes) {
            for (var attributeName in attributes) {
                if (attributes.hasOwnProperty(attributeName)) {
                    ss.setAttribute(attributeName, attributes[attributeName]);
                }
            }
        }
        ss.rel = "stylesheet";
        ss.href = href;
        ss.media = "only x";
        function ready(cb) {
            if (doc.body) {
                return cb();
            }
            setTimeout(function () {
                ready(cb);
            });
        }
        ready(function () {
            ref.parentNode.insertBefore(ss, (before ? ref : ref.nextSibling));
        });
        var onloadcssdefined = function (cb) {
            var resolvedHref = ss.href;
            var i = sheets.length;
            while (i--) {
                if (sheets[i].href === resolvedHref) {
                    return cb();
                }
            }
            setTimeout(function () {
                onloadcssdefined(cb);
            });
        };
        function loadCB() {
            if (ss.addEventListener) {
                ss.removeEventListener("load", loadCB);
            }
            ss.media = media || "all";
        }
        if (ss.addEventListener) {
            ss.addEventListener("load", loadCB);
        }
        ss.onloadcssdefined = onloadcssdefined;
        onloadcssdefined(loadCB);
        return ss;
    };
    
</script>
 

<!-- 轮播图所需要的js -->
<script src="/js/swiper/swiper.min.js"></script>
<script src="/js/swiper/vue-awesome-swiper.js"></script>
<script src="/js/swiper/swiper.animate1.0.3.min.js"></script>


  <script src="/js/vue-typed-js/index.js"></script>


<!-- 首页的公告滚动插件的js需要重新加载 -->
<script src="/js/vue-seamless-scroll/index.js"></script>

<!-- 打字机效果js -->
<script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11"></script>


    <!-- 内容区域 -->
    <div id="safearea">
      <main class="main" id="pjax-container" style="margin-top: 0;">
        
 <!-- prismjs 代码高亮 -->
 
    
    <link href="/js/prism/prism-line-numbers.css" rel="stylesheet">
    
        <link href="/js/prism/prism.min.css" rel="stylesheet">
    

    <style>
        pre[class*="language-"] {
            overflow-y: hidden;
        }
        .line-numbers .line-numbers-rows {
            border: none;
        }
    </style>
  


<div class="bg-dark-floor" style="position: fixed;left: 0;top: 0;width: 100%;height: 100%;z-index: -1;"></div>

<!-- 文章详情页顶部图片和标题 -->




<div class="post-detail-header lazyload" id="thumbnail_canvas" data-original="/medias/6.jpg" style="background-repeat: no-repeat; background-size: cover; 
  background-position: center center;position: relative;">
  <div class="post-detail-header-mask"></div>
  <canvas id="header_canvas"style="position:absolute;bottom:0;pointer-events:none;"></canvas>
  
  <div class="post-detail-header_info-box">
    <div class="title-box">
      <span class="title">
        在Docker中使用MySQL高可用之MHA
      </span>
    </div>
    
    
      
        <span class="post-detail-header_date">
          <i class="fa fa-calendar"></i> 发表于：2022-09-26 |
        </span>
      

      

      
        <div class="post-detail-header_wordcount">
          <span class="totalcount">
            <i class="fa fa-file-text-o"></i> 字数统计: 12.7k |
          </span>
  
          <span class="min2read">
            <i class="fa fa-clock-o"></i> 阅读时长: 73分钟 |
          </span>
  
          
            <span class="reading">
              <i class="fa fa-eye"></i> 阅读量：<span id="busuanzi_value_page_pv"></span>
            </span>
          
        </div>
      
    
  </div>
  
  
    <script src="/js/bubble/bubble.js"></script>
  
</div>



<div class="row justify-position">
  <div class="main-content">
    <article class="post post-detail">
      
      <div class="post-content">
        <p>目录<br>    一、MHA简介和架构<br>    1.1 MHA简介<br>    1.2 MHA工具包的组成<br>    1.3 MHA架构<br>    二、准备MHA环境<br>    2.1 下载MHA镜像<br>    2.2 编辑yml文件，创建MHA相关容器<br>    2.3 安装docker-compose软件（若已安装，可忽略）<br>    2.4 创建MHA容器<br>    2.5 初始化MHA环境<br>    2.5.1 添加网卡<br>    2.5.2 修改Manager节点的hosts文件<br>    2.5.3 主库131添加VIP<br>    2.5.4 分别进入132和133启动复制进程<br>    三、测试MHA相关功能<br>    3.1 检查MHA环境的配置<br>    3.1.1 检查SSH情况：<br>    3.1.2 检查复制情况：<br>    3.1.3 检查MHA状态：<br>    3.1.4 启动MHA Manager<br>    3.1.5 关闭MHA-manager<br>    3.2 测试场景一：自动故障转移+邮件告警<br>    3.2.1 启动客户端连接到VIP135，后端其实是连接到主库131<br>    3.2.2 模拟主库131宕机，即停止MySQL服务<br>    3.2.3 观察如下现象：<br>    3.2.4 启动131，恢复131为备库<br>    3.2.5 Switchover切换：手动切换131为主库，132为备库<br>    3.3 测试场景二：主库故障手动转移<br>    3.4 mysql-utilities包<br>    一、MHA简介和架构<br>    1.1 MHA简介<br>    MHA（Master High Availability Manager and tools for MySQL）目前在MySQL高可用方面是一个相对成熟的解决方案，它是由日本人youshimaton采用Perl语言编写的一个脚本管理工具。MHA是一套优秀的作为MySQL高可用性环境下故障切换和主从提升的高可用软件。MHA仅适用于MySQL Replication环境，目的在于维持Master主库的高可用性。在MySQL故障切换过程中，MHA能做到0~30秒之内自动完成数据库的故障切换操作，并且在进行故障切换的过程中，MHA能最大程度上保证数据库的一致性，以达到真正意义上的高可用。</p>
<pre><code>目前MHA主要支持一主多从的架构，要搭建MHA，要求一个复制集群必须最少有3台数据库服务器，一主二从，即一台充当Master，一台充当备用Master，另一台充当从库。

1.2 MHA工具包的组成
MHA由两部分组成：MHA Manager（管理节点）和MHA Node（数据节点）。MHA Manager可以独立部署在一台独立的机器上管理多个Master-Slave集群，也可以部署在一台Slave上。MHA Node运行在每台MySQL服务器上，MHA Manager会定时探测集群中Master节点。当Master出现故障时，它可以自动将具有最新数据的Slave提升为新的Master，然后将所有其他的Slave重新指向新的Master。整个故障转移过程对应用程序是完全透明的。MHA node运行在每台MySQL服务器上，它通过监控具备解析和清理logs功能的脚本来加快故障转移的。

Manager工具包情况如下：

masterha_check_ssh:检查MHA的SSH配置情况。
masterha_check_repl:检查MySQL复制状况。
masterha_manager:启动MHA。
masterha_check_status:检测当前MHA运行状态。
masterha_master_monitor:检测Master是否宕机。
masterha_master_switch:控制故障转移（自动或手动）。
masterha_conf_host:添加或删除配置的server信息。
Node工具包（通常由MHA Manager的脚本触发，无需人工操作）情况如下：l

save_binary_logs:保存和复制Master的binlog日志。
apply_diff_relay_logs:识别差异的中级日志时间并将其应用到其他Slave。
filter_mysqlbinlog:去除不必要的ROOLBACK事件（已经废弃）
purge_relay_logs:清除中继日志（不阻塞SQL线程）
1.3 MHA架构
本文所使用的MHA架构规划如下表：

IP	主机名	作用	Server ID	Port	类型	备注
192.168.68.131	MHA-LHR-Master1-ip131	master node	573306131	3306	写入	对外提供写服务
192.168.68.132	MHA-LHR-Slave1-ip132	slave node1 （Candicate Master）	573306132	读	备选Master提供读服务	
192.168.68.133	MHA-LHR-Slave2-ip133	slave node2	573306133	读	提供读服务	
192.168.68.134	MHA-LHR-Monitor-ip134	Monitor host				监控其它机器，一旦Mater宕机，将会把备选Master提升为新的Master，而将Slave指向新的Master
192.168.68.135		VIP				在131和132之间进行浮动漂移
MySQL数据库版本：MySQL 5.7.30，MySQL节点端口都是3306，各自的server_id不一样						
MHA切换前和切换后的架构图：
</code></pre>
<p><img "" class="lazyload placeholder" data-original="/images/pasted-82.png" src="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt=" "></p>
<p><img "" class="lazyload placeholder" data-original="/images/pasted-83.png" src="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt=" "></p>
<pre><code>二、准备MHA环境
2.1 下载MHA镜像
小麦苗的Docker Hub的地址：https://hub.docker.com/u/lhrbest
# 下载镜像
docker pull registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-master1-ip131
docker pull registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-slave1-ip132
docker pull registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-slave2-ip133
docker pull registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-monitor-ip134

# 重命名镜像
docker tag 	registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-master1-ip131  lhrbest/mha-lhr-master1-ip131
docker tag	registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-slave1-ip132   lhrbest/mha-lhr-slave1-ip132 
docker tag	registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-slave2-ip133   lhrbest/mha-lhr-slave2-ip133 
docker tag	registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-monitor-ip134  lhrbest/mha-lhr-monitor-ip134
一共4个镜像，3个MHA Node，一个MHA Manager，压缩包大概3G，下载完成后：

[root@lhrdocker ~]# docker images | grep mha
registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-monitor-ip134          latest              7d29597dc997        14 hours ago        1.53GB
registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-slave2-ip133           latest              d3717794e93a        40 hours ago        4.56GB
registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-slave1-ip132           latest              f62ee813e487        40 hours ago        4.56GB
registry.cn-hangzhou.aliyuncs.com/lhrbest/mha-lhr-master1-ip131          latest              ae7be48d83dc        40 hours ago        4.56GB
2.2 编辑yml文件，创建MHA相关容器
编辑yml文件，使用docker-compose来创建MHA相关容器，注意docker-compose.yml文件的格式，对空格、缩进、对齐都有严格要求：

# 创建存放yml文件的路径
mkdir -p /root/mha

# 编辑文件/root/mha/docker-compose.yml
cat &gt; /root/mha/docker-compose.yml &lt;&lt;&quot;EOF&quot;
version: &#39;3.8&#39;

services:
  MHA-LHR-Master1-ip131:
    container_name: &quot;MHA-LHR-Master1-ip131&quot;
    restart: &quot;always&quot;
    hostname: MHA-LHR-Master1-ip131
    privileged: true
    image: lhrbest/mha-lhr-master1-ip131
    ports:
      - &quot;33061:3306&quot;
      - &quot;2201:22&quot;
    networks:
      mhalhr:
        ipv4_address: 192.168.68.131

  MHA-LHR-Slave1-ip132:
    container_name: &quot;MHA-LHR-Slave1-ip132&quot;
    restart: &quot;always&quot;
    hostname: MHA-LHR-Slave1-ip132
    privileged: true
    image: lhrbest/mha-lhr-slave1-ip132
    ports:
      - &quot;33062:3306&quot;
      - &quot;2202:22&quot;
    networks:
      mhalhr:
        ipv4_address: 192.168.68.132

  MHA-LHR-Slave2-ip133:
    container_name: &quot;MHA-LHR-Slave2-ip133&quot;
    restart: &quot;always&quot;
    hostname: MHA-LHR-Master1-ip131
    privileged: true
    image: lhrbest/mha-lhr-slave2-ip133
    ports:
      - &quot;33063:3306&quot;
      - &quot;2203:22&quot;
    networks:
      mhalhr:
        ipv4_address: 192.168.68.133

  MHA-LHR-Monitor-ip134:
    container_name: &quot;MHA-LHR-Monitor-ip134&quot;
    restart: &quot;always&quot;
    hostname: MHA-LHR-Monitor-ip134
    privileged: true
    image: lhrbest/mha-lhr-monitor-ip134
    ports:
      - &quot;33064:3306&quot;
      - &quot;2204:22&quot;
    networks:
      mhalhr:
        ipv4_address: 192.168.68.134

networks:
  mhalhr:
    name: mhalhr
    ipam:
      config:
         - subnet: &quot;192.168.68.0/16&quot;

EOF

2.3 安装docker-compose软件（若已安装，可忽略）
安装 Docker Compose官方文档：https://docs.docker.com/compose/

编辑docker-compose.yml文件官方文档：https://docs.docker.com/compose/compose-file/

[root@lhrdocker ~]# curl --insecure -L https://github.com/docker/compose/releases/download/1.26.2/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   638  100   638    0     0    530      0  0:00:01  0:00:01 --:--:--   531
100 11.6M  100 11.6M    0     0  1994k      0  0:00:06  0:00:06 --:--:-- 2943k
[root@lhrdocker ~]# chmod +x /usr/local/bin/docker-compose
[root@lhrdocker ~]# docker-compose -v
docker-compose version 1.26.2, build eefe0d31
2.4 创建MHA容器
# 启动mha环境的容器，一定要进入文件夹/root/mha/后再操作
[root@lhrdocker ~]# cd /root/mha/
[root@lhrdocker mha]#
[root@lhrdocker mha]# docker-compose up -d
Creating network &quot;mhalhr&quot; with the default driver
Creating MHA-LHR-Monitor-ip134 ... done
Creating MHA-LHR-Slave2-ip133  ... done
Creating MHA-LHR-Master1-ip131 ... done
Creating MHA-LHR-Slave1-ip132  ... done
[root@lhrdocker mha]# docker ps
CONTAINER ID        IMAGE                           COMMAND             CREATED             STATUS              PORTS                                                            NAMES
d5b1af2ca979        lhrbest/mha-lhr-slave1-ip132    &quot;/usr/sbin/init&quot;    12 seconds ago      Up 9 seconds        16500-16599/tcp, 0.0.0.0:2202-&gt;22/tcp, 0.0.0.0:33062-&gt;3306/tcp   MHA-LHR-Slave1-ip132
8fa79f476aaa        lhrbest/mha-lhr-master1-ip131   &quot;/usr/sbin/init&quot;    12 seconds ago      Up 10 seconds       16500-16599/tcp, 0.0.0.0:2201-&gt;22/tcp, 0.0.0.0:33061-&gt;3306/tcp   MHA-LHR-Master1-ip131
74407b9df567        lhrbest/mha-lhr-slave2-ip133    &quot;/usr/sbin/init&quot;    12 seconds ago      Up 10 seconds       16500-16599/tcp, 0.0.0.0:2203-&gt;22/tcp, 0.0.0.0:33063-&gt;3306/tcp   MHA-LHR-Slave2-ip133
83f1cab03c9b        lhrbest/mha-lhr-monitor-ip134   &quot;/usr/sbin/init&quot;    12 seconds ago      Up 10 seconds       0.0.0.0:2204-&gt;22/tcp, 0.0.0.0:33064-&gt;3306/tcp                    MHA-LHR-Monitor-ip134
[root@lhrdocker mha]# 
2.5 初始化MHA环境
2.5.1 添加网卡
# 给MHA加入默认的网卡
docker network connect bridge MHA-LHR-Master1-ip131
docker network connect bridge MHA-LHR-Slave1-ip132
docker network connect bridge MHA-LHR-Slave2-ip133
docker network connect bridge MHA-LHR-Monitor-ip134
注意：请确保这4个节点的eth0都是192.168.68.0网段，否则后续的MHA切换可能会出问题。如果不一致，那么可以使用如下命令修改：

# 删除网卡
docker network disconnect bridge MHA-LHR-Master1-ip131
docker network disconnect mhalhr MHA-LHR-Master1-ip131

# 重启容器
docker restart MHA-LHR-Master1-ip131

# 添加网卡
docker network connect mhalhr MHA-LHR-Master1-ip131 --ip 192.168.68.131
docker network connect bridge MHA-LHR-Master1-ip131
2.5.2 修改Manager节点的hosts文件
# 进入管理节点134
docker exec -it MHA-LHR-Monitor-ip134 bash

# 修改/etc/hosts文件
cat &gt;&gt; /etc/hosts &lt;&lt; EOF
192.168.68.131  MHA-LHR-Master1-ip131
192.168.68.132  MHA-LHR-Slave1-ip132
192.168.68.133  MHA-LHR-Slave2-ip133
192.168.68.134  MHA-LHR-Monitor-ip134
EOF
2.5.3 主库131添加VIP
# 进入主库131
docker exec -it MHA-LHR-Master1-ip131 bash

# 添加VIP135
/sbin/ifconfig eth0:1 192.168.68.135/24
ifconfig
添加完成后：

[root@MHA-LHR-Master1-ip131 /]# ifconfig
eth0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 192.168.68.131  netmask 255.255.0.0  broadcast 192.168.255.255
        ether 02:42:c0:a8:44:83  txqueuelen 0  (Ethernet)
        RX packets 220  bytes 15883 (15.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 189  bytes 17524 (17.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0:1: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 192.168.68.135  netmask 255.255.255.0  broadcast 192.168.68.255
        ether 02:42:c0:a8:44:83  txqueuelen 0  (Ethernet)

eth1: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:02  txqueuelen 0  (Ethernet)
        RX packets 31  bytes 2697 (2.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 14  bytes 3317 (3.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73&lt;UP,LOOPBACK,RUNNING&gt;  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 5  bytes 400 (400.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5  bytes 400 (400.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        
# 管理节点已经可以ping通VIP了
[root@MHA-LHR-Monitor-ip134 /]# ping 192.168.68.135
PING 192.168.68.135 (192.168.68.135) 56(84) bytes of data.
64 bytes from 192.168.68.135: icmp_seq=1 ttl=64 time=0.172 ms
64 bytes from 192.168.68.135: icmp_seq=2 ttl=64 time=0.076 ms
^C
--- 192.168.68.135 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.076/0.124/0.172/0.048 ms
2.5.4 分别进入132和133启动复制进程
-- 132节点 
mysql -h192.168.59.220 -uroot -plhr -P33062
reset slave;
start slave;
show slave status \G

-- 133节点
mysql -h192.168.59.220 -uroot -plhr -P33063
reset slave;
start slave;
show slave status \G
结果：

C:\Users\lhrxxt&gt;mysql -h192.168.59.220 -uroot -plhr -P33062
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.30-log MySQL Community Server (GPL)

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type &#39;help;&#39; or &#39;\h&#39; for help. Type &#39;\c&#39; to clear the current input statement.

MySQL [(none)]&gt; start slave;
ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repository

MySQL [(none)]&gt; reset slave;
Query OK, 0 rows affected (0.02 sec)

MySQL [(none)]&gt; start slave;
Query OK, 0 rows affected (0.01 sec)

MySQL [(none)]&gt; show slave status \G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.68.131
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: MHA-LHR-Master1-ip131-bin.000011
          Read_Master_Log_Pos: 234
               Relay_Log_File: MHA-LHR-Master1-ip131-relay-bin.000003
                Relay_Log_Pos: 399
        Relay_Master_Log_File: MHA-LHR-Master1-ip131-bin.000011
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB: information_schema,performance_schema,mysql,sys
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 234
              Relay_Log_Space: 799
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 573306131
                  Master_UUID: c8ca4f1d-aec3-11ea-942b-0242c0a84483
             Master_Info_File: /usr/local/mysql-5.7.30-linux-glibc2.12-x86_64/data/master.info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set:
            Executed_Gtid_Set: c8ca4f1d-aec3-11ea-942b-0242c0a84483:1-11,
d24a77d1-aec3-11ea-9399-0242c0a84484:1-3
                Auto_Position: 1
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:
1 row in set (0.00 sec)
至此，我们就把MHA环境准备好了，接下来就开始测试MHA的各项功能。

三、测试MHA相关功能
在正式测试之前，我们要保证MHA环境已经配置正确，且MHA管理进程已经启动。

3.1 检查MHA环境的配置
在Manager节点检查SSH、复制及MHA的状态。

3.1.1 检查SSH情况：
[root@MHA-LHR-Monitor-ip134 /]# masterha_check_ssh --conf=/etc/mha/mha.cnf
Sat Aug  8 09:57:42 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sat Aug  8 09:57:42 2020 - [info] Reading application default configuration from /etc/mha/mha.cnf..
Sat Aug  8 09:57:42 2020 - [info] Reading server configuration from /etc/mha/mha.cnf..
Sat Aug  8 09:57:42 2020 - [info] Starting SSH connection tests..
Sat Aug  8 09:57:43 2020 - [debug] 
Sat Aug  8 09:57:42 2020 - [debug]  Connecting via SSH from root@192.168.68.131(192.168.68.131:22) to root@192.168.68.132(192.168.68.132:22)..
Sat Aug  8 09:57:42 2020 - [debug]   ok.
Sat Aug  8 09:57:42 2020 - [debug]  Connecting via SSH from root@192.168.68.131(192.168.68.131:22) to root@192.168.68.133(192.168.68.133:22)..
Sat Aug  8 09:57:42 2020 - [debug]   ok.
Sat Aug  8 09:57:43 2020 - [debug] 
Sat Aug  8 09:57:42 2020 - [debug]  Connecting via SSH from root@192.168.68.132(192.168.68.132:22) to root@192.168.68.131(192.168.68.131:22)..
Sat Aug  8 09:57:42 2020 - [debug]   ok.
Sat Aug  8 09:57:42 2020 - [debug]  Connecting via SSH from root@192.168.68.132(192.168.68.132:22) to root@192.168.68.133(192.168.68.133:22)..
Sat Aug  8 09:57:43 2020 - [debug]   ok.
Sat Aug  8 09:57:44 2020 - [debug] 
Sat Aug  8 09:57:43 2020 - [debug]  Connecting via SSH from root@192.168.68.133(192.168.68.133:22) to root@192.168.68.131(192.168.68.131:22)..
Sat Aug  8 09:57:43 2020 - [debug]   ok.
Sat Aug  8 09:57:43 2020 - [debug]  Connecting via SSH from root@192.168.68.133(192.168.68.133:22) to root@192.168.68.132(192.168.68.132:22)..
Sat Aug  8 09:57:43 2020 - [debug]   ok.
Sat Aug  8 09:57:44 2020 - [info] All SSH connection tests passed successfully.
结果“All SSH connection tests passed successfully.”表示MHA的3个数据节点之间的SSH是正常的。

3.1.2 检查复制情况：
[root@MHA-LHR-Monitor-ip134 /]# masterha_check_repl --conf=/etc/mha/mha.cnf
Sat Aug  8 09:59:31 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sat Aug  8 09:59:31 2020 - [info] Reading application default configuration from /etc/mha/mha.cnf..
Sat Aug  8 09:59:31 2020 - [info] Reading server configuration from /etc/mha/mha.cnf..
Sat Aug  8 09:59:31 2020 - [info] MHA::MasterMonitor version 0.58.
Sat Aug  8 09:59:33 2020 - [info] GTID failover mode = 1
Sat Aug  8 09:59:33 2020 - [info] Dead Servers:
Sat Aug  8 09:59:33 2020 - [info] Alive Servers:
Sat Aug  8 09:59:33 2020 - [info]   192.168.68.131(192.168.68.131:3306)
Sat Aug  8 09:59:33 2020 - [info]   192.168.68.132(192.168.68.132:3306)
Sat Aug  8 09:59:33 2020 - [info]   192.168.68.133(192.168.68.133:3306)
Sat Aug  8 09:59:33 2020 - [info] Alive Slaves:
Sat Aug  8 09:59:33 2020 - [info]   192.168.68.132(192.168.68.132:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 09:59:33 2020 - [info]     GTID ON
Sat Aug  8 09:59:33 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 09:59:33 2020 - [info]     Primary candidate for the new Master (candidate_master is set)
Sat Aug  8 09:59:33 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 09:59:33 2020 - [info]     GTID ON
Sat Aug  8 09:59:33 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 09:59:33 2020 - [info] Current Alive Master: 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 09:59:33 2020 - [info] Checking slave configurations..
Sat Aug  8 09:59:33 2020 - [info]  read_only=1 is not set on slave 192.168.68.132(192.168.68.132:3306).
Sat Aug  8 09:59:33 2020 - [info]  read_only=1 is not set on slave 192.168.68.133(192.168.68.133:3306).
Sat Aug  8 09:59:33 2020 - [info] Checking replication filtering settings..
Sat Aug  8 09:59:33 2020 - [info]  binlog_do_db= , binlog_ignore_db= information_schema,mysql,performance_schema,sys
Sat Aug  8 09:59:33 2020 - [info]  Replication filtering check ok.
Sat Aug  8 09:59:33 2020 - [info] GTID (with auto-pos) is supported. Skipping all SSH and Node package checking.
Sat Aug  8 09:59:33 2020 - [info] Checking SSH publickey authentication settings on the current master..
Sat Aug  8 09:59:33 2020 - [info] HealthCheck: SSH to 192.168.68.131 is reachable.
Sat Aug  8 09:59:33 2020 - [info] 
192.168.68.131(192.168.68.131:3306) (current master)
 +--192.168.68.132(192.168.68.132:3306)
 +--192.168.68.133(192.168.68.133:3306)

Sat Aug  8 09:59:33 2020 - [info] Checking replication health on 192.168.68.132..
Sat Aug  8 09:59:33 2020 - [info]  ok.
Sat Aug  8 09:59:33 2020 - [info] Checking replication health on 192.168.68.133..
Sat Aug  8 09:59:33 2020 - [info]  ok.
Sat Aug  8 09:59:33 2020 - [info] Checking master_ip_failover_script status:
Sat Aug  8 09:59:33 2020 - [info]   /usr/local/mha/scripts/master_ip_failover --command=status --ssh_user=root --orig_master_host=192.168.68.131 --orig_master_ip=192.168.68.131 --orig_master_port=3306 


IN SCRIPT TEST====/sbin/ip addr del 192.168.68.135/24 dev eth0==/sbin/ifconfig eth0:1 192.168.68.135/24===

Checking the Status of the script.. OK 
Sat Aug  8 09:59:33 2020 - [info]  OK.
Sat Aug  8 09:59:33 2020 - [warning] shutdown_script is not defined.
Sat Aug  8 09:59:33 2020 - [info] Got exit code 0 (Not master dead).

MySQL Replication Health is OK.
“MySQL Replication Health is OK.”表示1主2从的架构目前是正常的。

3.1.3 检查MHA状态：
[root@MHA-LHR-Monitor-ip134 /]# masterha_check_status --conf=/etc/mha/mha.cnf
mha is stopped(2:NOT_RUNNING).
注意：如果正常，会显示“PING_OK&quot;，否则会显示“NOT_RUNNING&quot;，这代表MHA监控没有开启。

3.1.4 启动MHA Manager
[root@MHA-LHR-Monitor-ip134 /]# nohup masterha_manager --conf=/etc/mha/mha.cnf  --ignore_last_failover &lt; /dev/null &gt; /usr/local/mha/manager_start.log 2&gt;&amp;1 &amp;
[1] 216
[root@MHA-LHR-Monitor-ip134 /]# masterha_check_status --conf=/etc/mha/mha.cnf                                                                               
mha (pid:216) is running(0:PING_OK), master:192.168.68.131
检查结果显示“PING_OK”，表示MHA监控软件已经启动了，主库为192.168.68.131。

启动参数介绍：

--remove_dead_master_conf 该参数代表当发生主从切换后，老的主库的IP将会从配置文件中移除。

--manger_log 日志存放位置

--ignore_last_failover 在缺省情况下，如果MHA检测到连续发生宕机，且两次宕机间隔不足8小时的话，则不会进行Failover，之所以这样限制是为了避免ping-pong效应。该参数代表忽略上次MHA触发切换产生的文件，默认情况下，MHA发生切换后会在日志目录下产生mha.failover.complete文件，下次再次切换的时候如果发现该目录下存在该文件将不允许触发切换，除非在第一次切换后收到删除该文件，为了方便，这里设置为--ignore_last_failover。

注意，一旦自动failover发生，mha manager就停止监控了，如果需要请手动再次开启。

3.1.5 关闭MHA-manager
masterha_stop --conf=/etc/mha/mha.cnf
我们当然不关闭，不能执行这句哟。

3.2 测试场景一：自动故障转移+邮件告警
自动故障转移后的架构如下图所示：

image-20210201084854422

按照以下流程测试：

3.2.1 启动客户端连接到VIP135，后端其实是连接到主库131
[root@lhrdocker ~]# mysql -uroot -plhr -h192.168.68.135 -P3306
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 5.7.30-log MySQL Community Server (GPL)

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type &#39;help;&#39; or &#39;\h&#39; for help. Type &#39;\c&#39; to clear the current input statement.

mysql&gt; select @@hostname;
+-----------------------+
| @@hostname            |
+-----------------------+
| MHA-LHR-Master1-ip131 |
+-----------------------+
1 row in set (0.00 sec)

mysql&gt; show slave hosts;
+-----------+----------------+------+-----------+--------------------------------------+
| Server_id | Host           | Port | Master_id | Slave_UUID                           |
+-----------+----------------+------+-----------+--------------------------------------+
| 573306133 | 192.168.68.133 | 3306 | 573306131 | d391ce7e-aec3-11ea-94cd-0242c0a84485 |
| 573306132 | 192.168.68.132 | 3306 | 573306131 | d24a77d1-aec3-11ea-9399-0242c0a84484 |
+-----------+----------------+------+-----------+--------------------------------------+
2 rows in set (0.00 sec)
3.2.2 模拟主库131宕机，即停止MySQL服务
docker stop MHA-LHR-Master1-ip131
3.2.3 观察如下现象：
①　VIP135自动漂移到132

[root@MHA-LHR-Slave1-ip132 /]# ifconfig
eth0: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 192.168.68.132  netmask 255.255.0.0  broadcast 192.168.255.255
        ether 02:42:c0:a8:44:84  txqueuelen 0  (Ethernet)
        RX packets 411  bytes 58030 (56.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 343  bytes 108902 (106.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

eth0:1: flags=4163&lt;UP,BROADCAST,RUNNING,MULTICAST&gt;  mtu 1500
        inet 192.168.68.135  netmask 255.255.255.0  broadcast 192.168.68.255
        ether 02:42:c0:a8:44:84  txqueuelen 0  (Ethernet)
②　主库自动变为132，命令为：show slave hosts;

mysql&gt; select @@hostname;
ERROR 2013 (HY000): Lost connection to MySQL server during query
mysql&gt; select @@hostname;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id:    15
Current database: *** NONE ***

+----------------------+
| @@hostname           |
+----------------------+
| MHA-LHR-Slave1-ip132 |
+----------------------+
1 row in set (0.00 sec)

mysql&gt; show slave hosts;
+-----------+----------------+------+-----------+--------------------------------------+
| Server_id | Host           | Port | Master_id | Slave_UUID                           |
+-----------+----------------+------+-----------+--------------------------------------+
| 573306133 | 192.168.68.133 | 3306 | 573306132 | d391ce7e-aec3-11ea-94cd-0242c0a84485 |
+-----------+----------------+------+-----------+--------------------------------------+
1 row in set (0.00 sec)
③　MHA进程自动停止

[1]+  Done                    nohup masterha_manager --conf=/etc/mha/mha.cnf --ignore_last_failover &lt; /dev/null &gt; /usr/local/mha/manager_start.log 2&gt;&amp;1
[root@MHA-LHR-Monitor-ip134 /]# 
[root@MHA-LHR-Monitor-ip134 /]# 
[root@MHA-LHR-Monitor-ip134 /]# ps -ef|grep mha
root        486    120  0 11:03 pts/0    00:00:00 grep --color=auto mha
④ MHA切换过程日志：

[root@MHA-LHR-Monitor-ip134 /]# tailf /usr/local/mha/manager_running.log

Sat Aug  8 11:01:23 2020 - [warning] Got error on MySQL select ping: 2013 (Lost connection to MySQL server during query)
Sat Aug  8 11:01:23 2020 - [info] Executing secondary network check script: /usr/local/bin/masterha_secondary_check -s MHA-LHR-Slave1-ip132 -s MHA-LHR-Slave2-ip133 --user=root --master_host=MHA-LHR-Master1-ip131 --master_ip=192.168.68.131 --master_port=3306  --user=root  --master_host=192.168.68.131  --master_ip=192.168.68.131  --master_port=3306 --master_user=mha --master_password=lhr --ping_type=SELECT
Sat Aug  8 11:01:23 2020 - [info] Executing SSH check script: exit 0
Sat Aug  8 11:01:23 2020 - [warning] HealthCheck: SSH to 192.168.68.131 is NOT reachable.
Monitoring server MHA-LHR-Slave1-ip132 is reachable, Master is not reachable from MHA-LHR-Slave1-ip132. OK.
Monitoring server MHA-LHR-Slave2-ip133 is reachable, Master is not reachable from MHA-LHR-Slave2-ip133. OK.
Sat Aug  8 11:01:23 2020 - [info] Master is not reachable from all other monitoring servers. Failover should start.
Sat Aug  8 11:01:24 2020 - [warning] Got error on MySQL connect: 2003 (Can&#39;t connect to MySQL server on &#39;192.168.68.131&#39; (4))
Sat Aug  8 11:01:24 2020 - [warning] Connection failed 2 time(s)..
Sat Aug  8 11:01:25 2020 - [warning] Got error on MySQL connect: 2003 (Can&#39;t connect to MySQL server on &#39;192.168.68.131&#39; (4))
Sat Aug  8 11:01:25 2020 - [warning] Connection failed 3 time(s)..
Sat Aug  8 11:01:26 2020 - [warning] Got error on MySQL connect: 2003 (Can&#39;t connect to MySQL server on &#39;192.168.68.131&#39; (4))
Sat Aug  8 11:01:26 2020 - [warning] Connection failed 4 time(s)..
Sat Aug  8 11:01:26 2020 - [warning] Master is not reachable from health checker!
Sat Aug  8 11:01:26 2020 - [warning] Master 192.168.68.131(192.168.68.131:3306) is not reachable!
Sat Aug  8 11:01:26 2020 - [warning] SSH is NOT reachable.
Sat Aug  8 11:01:26 2020 - [info] Connecting to a master server failed. Reading configuration file /etc/masterha_default.cnf and /etc/mha/mha.cnf again, and trying to connect to all servers to check server status..
Sat Aug  8 11:01:26 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sat Aug  8 11:01:26 2020 - [info] Reading application default configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:01:26 2020 - [info] Reading server configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:01:27 2020 - [info] GTID failover mode = 1
Sat Aug  8 11:01:27 2020 - [info] Dead Servers:
Sat Aug  8 11:01:27 2020 - [info]   192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:27 2020 - [info] Alive Servers:
Sat Aug  8 11:01:27 2020 - [info]   192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:01:27 2020 - [info]   192.168.68.133(192.168.68.133:3306)
Sat Aug  8 11:01:27 2020 - [info] Alive Slaves:
Sat Aug  8 11:01:27 2020 - [info]   192.168.68.132(192.168.68.132:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:27 2020 - [info]     GTID ON
Sat Aug  8 11:01:27 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:27 2020 - [info]     Primary candidate for the new Master (candidate_master is set)
Sat Aug  8 11:01:27 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:27 2020 - [info]     GTID ON
Sat Aug  8 11:01:27 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:27 2020 - [info] Checking slave configurations..
Sat Aug  8 11:01:27 2020 - [info]  read_only=1 is not set on slave 192.168.68.132(192.168.68.132:3306).
Sat Aug  8 11:01:27 2020 - [info]  read_only=1 is not set on slave 192.168.68.133(192.168.68.133:3306).
Sat Aug  8 11:01:27 2020 - [info] Checking replication filtering settings..
Sat Aug  8 11:01:27 2020 - [info]  Replication filtering check ok.
Sat Aug  8 11:01:27 2020 - [info] Master is down!
Sat Aug  8 11:01:27 2020 - [info] Terminating monitoring script.
Sat Aug  8 11:01:27 2020 - [info] Got exit code 20 (Master dead).
Sat Aug  8 11:01:27 2020 - [info] MHA::MasterFailover version 0.58.
Sat Aug  8 11:01:27 2020 - [info] Starting master failover.
Sat Aug  8 11:01:27 2020 - [info] 
Sat Aug  8 11:01:27 2020 - [info] * Phase 1: Configuration Check Phase..
Sat Aug  8 11:01:27 2020 - [info] 
Sat Aug  8 11:01:29 2020 - [info] GTID failover mode = 1
Sat Aug  8 11:01:29 2020 - [info] Dead Servers:
Sat Aug  8 11:01:29 2020 - [info]   192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:29 2020 - [info] Checking master reachability via MySQL(double check)...
Sat Aug  8 11:01:30 2020 - [info]  ok.
Sat Aug  8 11:01:30 2020 - [info] Alive Servers:
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.133(192.168.68.133:3306)
Sat Aug  8 11:01:30 2020 - [info] Alive Slaves:
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.132(192.168.68.132:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info]     Primary candidate for the new Master (candidate_master is set)
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info] Starting GTID based failover.
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] ** Phase 1: Configuration Check Phase completed.
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 2: Dead Master Shutdown Phase..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] Forcing shutdown so that applications never connect to the current master..
Sat Aug  8 11:01:30 2020 - [info] Executing master IP deactivation script:
Sat Aug  8 11:01:30 2020 - [info]   /usr/local/mha/scripts/master_ip_failover --orig_master_host=192.168.68.131 --orig_master_ip=192.168.68.131 --orig_master_port=3306 --command=stop 


IN SCRIPT TEST====/sbin/ip addr del 192.168.68.135/24 dev eth0==/sbin/ifconfig eth0:1 192.168.68.135/24===

Disabling the VIP on old master: 192.168.68.131 
Sat Aug  8 11:01:30 2020 - [info]  done.
Sat Aug  8 11:01:30 2020 - [warning] shutdown_script is not set. Skipping explicit shutting down of the dead master.
Sat Aug  8 11:01:30 2020 - [info] * Phase 2: Dead Master Shutdown Phase completed.
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 3: Master Recovery Phase..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 3.1: Getting Latest Slaves Phase..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] The latest binary log file/position on all slaves is MHA-LHR-Master1-ip131-bin.000011:234
Sat Aug  8 11:01:30 2020 - [info] Latest slaves (Slaves that received relay log files to the latest):
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.132(192.168.68.132:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info]     Primary candidate for the new Master (candidate_master is set)
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info] The oldest binary log file/position on all slaves is MHA-LHR-Master1-ip131-bin.000011:234
Sat Aug  8 11:01:30 2020 - [info] Oldest slaves:
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.132(192.168.68.132:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info]     Primary candidate for the new Master (candidate_master is set)
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 3.3: Determining New Master Phase..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] Searching new master from slaves..
Sat Aug  8 11:01:30 2020 - [info]  Candidate masters from the configuration file:
Sat Aug  8 11:01:30 2020 - [info]   192.168.68.132(192.168.68.132:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:01:30 2020 - [info]     GTID ON
Sat Aug  8 11:01:30 2020 - [info]     Replicating from 192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:01:30 2020 - [info]     Primary candidate for the new Master (candidate_master is set)
Sat Aug  8 11:01:30 2020 - [info]  Non-candidate masters:
Sat Aug  8 11:01:30 2020 - [info]  Searching from candidate_master slaves which have received the latest relay log events..
Sat Aug  8 11:01:30 2020 - [info] New master is 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:01:30 2020 - [info] Starting master failover..
Sat Aug  8 11:01:30 2020 - [info] 
From:
192.168.68.131(192.168.68.131:3306) (current master)
 +--192.168.68.132(192.168.68.132:3306)
 +--192.168.68.133(192.168.68.133:3306)

To:
192.168.68.132(192.168.68.132:3306) (new master)
 +--192.168.68.133(192.168.68.133:3306)
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 3.3: New Master Recovery Phase..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info]  Waiting all logs to be applied.. 
Sat Aug  8 11:01:30 2020 - [info]   done.
Sat Aug  8 11:01:30 2020 - [info] Getting new master&#39;s binlog name and position..
Sat Aug  8 11:01:30 2020 - [info]  MHA-LHR-Slave1-ip132-bin.000008:234
Sat Aug  8 11:01:30 2020 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST=&#39;192.168.68.132&#39;, MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER=&#39;repl&#39;, MASTER_PASSWORD=&#39;xxx&#39;;
Sat Aug  8 11:01:30 2020 - [info] Master Recovery succeeded. File:Pos:Exec_Gtid_Set: MHA-LHR-Slave1-ip132-bin.000008, 234, c8ca4f1d-aec3-11ea-942b-0242c0a84483:1-11,
d24a77d1-aec3-11ea-9399-0242c0a84484:1-3
Sat Aug  8 11:01:30 2020 - [info] Executing master IP activate script:
Sat Aug  8 11:01:30 2020 - [info]   /usr/local/mha/scripts/master_ip_failover --command=start --ssh_user=root --orig_master_host=192.168.68.131 --orig_master_ip=192.168.68.131 --orig_master_port=3306 --new_master_host=192.168.68.132 --new_master_ip=192.168.68.132 --new_master_port=3306 --new_master_user=&#39;mha&#39;   --new_master_password=xxx
Unknown option: new_master_user
Unknown option: new_master_password


IN SCRIPT TEST====/sbin/ip addr del 192.168.68.135/24 dev eth0==/sbin/ifconfig eth0:1 192.168.68.135/24===

Enabling the VIP - 192.168.68.135/24 on the new master - 192.168.68.132 
Sat Aug  8 11:01:30 2020 - [info]  OK.
Sat Aug  8 11:01:30 2020 - [info] ** Finished master recovery successfully.
Sat Aug  8 11:01:30 2020 - [info] * Phase 3: Master Recovery Phase completed.
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 4: Slaves Recovery Phase..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] * Phase 4.1: Starting Slaves in parallel..
Sat Aug  8 11:01:30 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info] -- Slave recovery on host 192.168.68.133(192.168.68.133:3306) started, pid: 474. Check tmp log /usr/local/mha/192.168.68.133_3306_20200808110127.log if it takes time..
Sat Aug  8 11:01:32 2020 - [info] 
Sat Aug  8 11:01:32 2020 - [info] Log messages from 192.168.68.133 ...
Sat Aug  8 11:01:32 2020 - [info] 
Sat Aug  8 11:01:30 2020 - [info]  Resetting slave 192.168.68.133(192.168.68.133:3306) and starting replication from the new master 192.168.68.132(192.168.68.132:3306)..
Sat Aug  8 11:01:30 2020 - [info]  Executed CHANGE MASTER.
Sat Aug  8 11:01:31 2020 - [info]  Slave started.
Sat Aug  8 11:01:31 2020 - [info]  gtid_wait(c8ca4f1d-aec3-11ea-942b-0242c0a84483:1-11,
d24a77d1-aec3-11ea-9399-0242c0a84484:1-3) completed on 192.168.68.133(192.168.68.133:3306). Executed 0 events.
Sat Aug  8 11:01:32 2020 - [info] End of log messages from 192.168.68.133.
Sat Aug  8 11:01:32 2020 - [info] -- Slave on host 192.168.68.133(192.168.68.133:3306) started.
Sat Aug  8 11:01:32 2020 - [info] All new slave servers recovered successfully.
Sat Aug  8 11:01:32 2020 - [info] 
Sat Aug  8 11:01:32 2020 - [info] * Phase 5: New master cleanup phase..
Sat Aug  8 11:01:32 2020 - [info] 
Sat Aug  8 11:01:32 2020 - [info] Resetting slave info on the new master..
Sat Aug  8 11:01:32 2020 - [info]  192.168.68.132: Resetting slave info succeeded.
Sat Aug  8 11:01:32 2020 - [info] Master failover to 192.168.68.132(192.168.68.132:3306) completed successfully.
Sat Aug  8 11:01:32 2020 - [info] 

----- Failover Report -----

mha: MySQL Master failover 192.168.68.131(192.168.68.131:3306) to 192.168.68.132(192.168.68.132:3306) succeeded

Master 192.168.68.131(192.168.68.131:3306) is down!

Check MHA Manager logs at MHA-LHR-Monitor-ip134:/usr/local/mha/manager_running.log for details.

Started automated(non-interactive) failover.
Invalidated master IP address on 192.168.68.131(192.168.68.131:3306)
Selected 192.168.68.132(192.168.68.132:3306) as a new master.
192.168.68.132(192.168.68.132:3306): OK: Applying all logs succeeded.
192.168.68.132(192.168.68.132:3306): OK: Activated master IP address.
192.168.68.133(192.168.68.133:3306): OK: Slave started, replicating from 192.168.68.132(192.168.68.132:3306)
192.168.68.132(192.168.68.132:3306): Resetting slave info succeeded.
Master failover to 192.168.68.132(192.168.68.132:3306) completed successfully.
Sat Aug  8 11:01:32 2020 - [info] Sending mail..

⑤ 同时，邮件收到告警

image-20210201085024988

注意：

1、首先确保你的134环境可以上外网

[root@MHA-LHR-Monitor-ip134 /]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=127 time=109 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=127 time=152 ms
64 bytes from 8.8.8.8: icmp_seq=3 ttl=127 time=132 ms
^C
--- 8.8.8.8 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 109.295/131.671/152.728/17.758 ms
[root@MHA-LHR-Monitor-ip134 /]# 
2、如果你想修改邮件的收件人，那么需要修改134管理节点的/usr/local/bin/send_report文件，将其中的lhrbest@qq.com修改为收件人的邮箱地址即可。文件/usr/local/bin/send_report内容如下所示：

[root@MHA-LHR-Monitor-ip134 /]# cat /usr/local/bin/send_report
#!/bin/bash
start_num=+`awk &#39;/Failover Report/&#123;print NR&#125;&#39; /usr/local/mha/manager_running.log | tail -n 1`
tail -n $start_num /usr/local/mha/manager_running.log | mail  -s  &#39;【严重告警】&#39;管理节点`hostname -s`&#39;的MHA架构发生了自动切换&#39; -a &#39;/usr/local/mha/manager_running.log&#39; lhrbest@qq.com
[root@MHA-LHR-Monitor-ip134 /]# 
3.2.4 启动131，恢复131为备库
# 启动131
docker start MHA-LHR-Master1-ip131

# 在134的日志文件中找到恢复的语句
grep &quot;All other slaves should start replication from here&quot; /usr/local/mha/manager_running.log

# 在131上执行恢复
CHANGE MASTER TO MASTER_HOST=&#39;192.168.68.132&#39;, MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER=&#39;repl&#39;, MASTER_PASSWORD=&#39;lhr&#39;;

start slave;
show slave status;

# 在134上检查
masterha_check_repl --conf=/etc/mha/mha.cnf
执行过程：

[root@MHA-LHR-Monitor-ip134 /]# grep &quot;All other slaves should start replication from here&quot; /usr/local/mha/manager_running.log
Mon Jun 15 14:16:31 2020 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST=&#39;192.168.68.132&#39;, MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER=&#39;repl&#39;, MASTER_PASSWORD=&#39;xxx&#39;;
Sat Aug  8 11:01:30 2020 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST=&#39;192.168.68.132&#39;, MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER=&#39;repl&#39;, MASTER_PASSWORD=&#39;xxx&#39;;
[root@MHA-LHR-Monitor-ip134 /]# 
[root@MHA-LHR-Monitor-ip134 /]# masterha_check_repl --conf=/etc/mha/mha.cnf
Sat Aug  8 11:23:30 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sat Aug  8 11:23:30 2020 - [info] Reading application default configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:23:30 2020 - [info] Reading server configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:23:30 2020 - [info] MHA::MasterMonitor version 0.58.
Sat Aug  8 11:23:32 2020 - [info] GTID failover mode = 1
Sat Aug  8 11:23:32 2020 - [info] Dead Servers:
Sat Aug  8 11:23:32 2020 - [info] Alive Servers:
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.133(192.168.68.133:3306)
Sat Aug  8 11:23:32 2020 - [info] Alive Slaves:
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.131(192.168.68.131:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:23:32 2020 - [info]     GTID ON
Sat Aug  8 11:23:32 2020 - [info]     Replicating from 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:23:32 2020 - [info]     GTID ON
Sat Aug  8 11:23:32 2020 - [info]     Replicating from 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info] Current Alive Master: 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info] Checking slave configurations..
Sat Aug  8 11:23:32 2020 - [info]  read_only=1 is not set on slave 192.168.68.131(192.168.68.131:3306).
Sat Aug  8 11:23:32 2020 - [info]  read_only=1 is not set on slave 192.168.68.133(192.168.68.133:3306).
Sat Aug  8 11:23:32 2020 - [info] Checking replication filtering settings..
Sat Aug  8 11:23:32 2020 - [info]  binlog_do_db= , binlog_ignore_db= information_schema,mysql,performance_schema,sys
Sat Aug  8 11:23:32 2020 - [info]  Replication filtering check ok.
Sat Aug  8 11:23:32 2020 - [info] GTID (with auto-pos) is supported. Skipping all SSH and Node package checking.
Sat Aug  8 11:23:32 2020 - [info] Checking SSH publickey authentication settings on the current master..
Sat Aug  8 11:23:32 2020 - [info] HealthCheck: SSH to 192.168.68.132 is reachable.
Sat Aug  8 11:23:32 2020 - [info] 
192.168.68.132(192.168.68.132:3306) (current master)
 +--192.168.68.131(192.168.68.131:3306)
 +--192.168.68.133(192.168.68.133:3306)

Sat Aug  8 11:23:32 2020 - [info] Checking replication health on 192.168.68.131..
Sat Aug  8 11:23:32 2020 - [info]  ok.
Sat Aug  8 11:23:32 2020 - [info] Checking replication health on 192.168.68.133..
Sat Aug  8 11:23:32 2020 - [info]  ok.
Sat Aug  8 11:23:32 2020 - [info] Checking master_ip_failover_script status:
Sat Aug  8 11:23:32 2020 - [info]   /usr/local/mha/scripts/master_ip_failover --command=status --ssh_user=root --orig_master_host=192.168.68.132 --orig_master_ip=192.168.68.132 --orig_master_port=3306 


IN SCRIPT TEST====/sbin/ip addr del 192.168.68.135/24 dev eth0==/sbin/ifconfig eth0:1 192.168.68.135/24===

Checking the Status of the script.. OK 
Sat Aug  8 11:23:32 2020 - [info]  OK.
Sat Aug  8 11:23:32 2020 - [warning] shutdown_script is not defined.
Sat Aug  8 11:23:32 2020 - [info] Got exit code 0 (Not master dead).

MySQL Replication Health is OK.
3.2.5 Switchover切换：手动切换131为主库，132为备库
类似Oracle DG中的switchover。在该场景下，主库并没有宕机。在主库活着的时候，将主库降级为备库，将备用主库提升为主库，并且重新配置主从关系。此时，MHA进程不能启动。

masterha_master_switch --conf=/etc/mha/mha.cnf  --master_state=alive \
--orig_master_is_new_slave --running_updates_limit=10000 --interactive=0 \
--new_master_host=192.168.68.131 --new_master_port=3306
参数解释：

--interactive 为是否交互，即你要输入yes或no

--running_updates_limit 如果在切换过程中不指定running_updates_limit，那么默认情况下running_updates_limit为1秒。故障切换时，候选master如果有延迟的话，mha切换不能成功，加上此参数表示延迟在此时间范围内都可切换（单位为s），但是切换的时间长短是由recover时relay日志的大小决定

--orig_master_is_new_slave 将原来的主降低为从并重新加入主从关系

--new_master_host 指定新的主库的主机名，建议写IP地址

--new_master_port 指定新的主库上mysql服务的端口

在切换完成后，主库为131，备库为132和133，VIP自动切换到131，即回到了最初的MHA状态。

[root@MHA-LHR-Monitor-ip134 /]# masterha_check_repl --conf=/etc/mha/mha.cnf
Sat Aug  8 11:23:30 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sat Aug  8 11:23:30 2020 - [info] Reading application default configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:23:30 2020 - [info] Reading server configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:23:30 2020 - [info] MHA::MasterMonitor version 0.58.
Sat Aug  8 11:23:32 2020 - [info] GTID failover mode = 1
Sat Aug  8 11:23:32 2020 - [info] Dead Servers:
Sat Aug  8 11:23:32 2020 - [info] Alive Servers:
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.131(192.168.68.131:3306)
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.133(192.168.68.133:3306)
Sat Aug  8 11:23:32 2020 - [info] Alive Slaves:
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.131(192.168.68.131:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:23:32 2020 - [info]     GTID ON
Sat Aug  8 11:23:32 2020 - [info]     Replicating from 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:23:32 2020 - [info]     GTID ON
Sat Aug  8 11:23:32 2020 - [info]     Replicating from 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info] Current Alive Master: 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:23:32 2020 - [info] Checking slave configurations..
Sat Aug  8 11:23:32 2020 - [info]  read_only=1 is not set on slave 192.168.68.131(192.168.68.131:3306).
Sat Aug  8 11:23:32 2020 - [info]  read_only=1 is not set on slave 192.168.68.133(192.168.68.133:3306).
Sat Aug  8 11:23:32 2020 - [info] Checking replication filtering settings..
Sat Aug  8 11:23:32 2020 - [info]  binlog_do_db= , binlog_ignore_db= information_schema,mysql,performance_schema,sys
Sat Aug  8 11:23:32 2020 - [info]  Replication filtering check ok.
Sat Aug  8 11:23:32 2020 - [info] GTID (with auto-pos) is supported. Skipping all SSH and Node package checking.
Sat Aug  8 11:23:32 2020 - [info] Checking SSH publickey authentication settings on the current master..
Sat Aug  8 11:23:32 2020 - [info] HealthCheck: SSH to 192.168.68.132 is reachable.
Sat Aug  8 11:23:32 2020 - [info] 
192.168.68.132(192.168.68.132:3306) (current master)
 +--192.168.68.131(192.168.68.131:3306)
 +--192.168.68.133(192.168.68.133:3306)

Sat Aug  8 11:23:32 2020 - [info] Checking replication health on 192.168.68.131..
Sat Aug  8 11:23:32 2020 - [info]  ok.
Sat Aug  8 11:23:32 2020 - [info] Checking replication health on 192.168.68.133..
Sat Aug  8 11:23:32 2020 - [info]  ok.
Sat Aug  8 11:23:32 2020 - [info] Checking master_ip_failover_script status:
Sat Aug  8 11:23:32 2020 - [info]   /usr/local/mha/scripts/master_ip_failover --command=status --ssh_user=root --orig_master_host=192.168.68.132 --orig_master_ip=192.168.68.132 --orig_master_port=3306 


IN SCRIPT TEST====/sbin/ip addr del 192.168.68.135/24 dev eth0==/sbin/ifconfig eth0:1 192.168.68.135/24===

Checking the Status of the script.. OK 
Sat Aug  8 11:23:32 2020 - [info]  OK.
Sat Aug  8 11:23:32 2020 - [warning] shutdown_script is not defined.
Sat Aug  8 11:23:32 2020 - [info] Got exit code 0 (Not master dead).

MySQL Replication Health is OK.
[root@MHA-LHR-Monitor-ip134 /]# masterha_master_switch --conf=/etc/mha/mha.cnf  --master_state=alive \
&gt; --orig_master_is_new_slave --running_updates_limit=10000 --interactive=0 \
&gt; --new_master_host=192.168.68.131 --new_master_port=3306
Sat Aug  8 11:26:36 2020 - [info] MHA::MasterRotate version 0.58.
Sat Aug  8 11:26:36 2020 - [info] Starting online master switch..
Sat Aug  8 11:26:36 2020 - [info] 
Sat Aug  8 11:26:36 2020 - [info] * Phase 1: Configuration Check Phase..
Sat Aug  8 11:26:36 2020 - [info] 
Sat Aug  8 11:26:36 2020 - [warning] Global configuration file /etc/masterha_default.cnf not found. Skipping.
Sat Aug  8 11:26:36 2020 - [info] Reading application default configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:26:36 2020 - [info] Reading server configuration from /etc/mha/mha.cnf..
Sat Aug  8 11:26:37 2020 - [info] GTID failover mode = 1
Sat Aug  8 11:26:37 2020 - [info] Current Alive Master: 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:26:37 2020 - [info] Alive Slaves:
Sat Aug  8 11:26:37 2020 - [info]   192.168.68.131(192.168.68.131:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:26:37 2020 - [info]     GTID ON
Sat Aug  8 11:26:37 2020 - [info]     Replicating from 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:26:37 2020 - [info]   192.168.68.133(192.168.68.133:3306)  Version=5.7.30-log (oldest major version between slaves) log-bin:enabled
Sat Aug  8 11:26:37 2020 - [info]     GTID ON
Sat Aug  8 11:26:37 2020 - [info]     Replicating from 192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:26:37 2020 - [info] Executing FLUSH NO_WRITE_TO_BINLOG TABLES. This may take long time..
Sat Aug  8 11:26:37 2020 - [info]  ok.
Sat Aug  8 11:26:37 2020 - [info] Checking MHA is not monitoring or doing failover..
Sat Aug  8 11:26:37 2020 - [info] Checking replication health on 192.168.68.131..
Sat Aug  8 11:26:37 2020 - [info]  ok.
Sat Aug  8 11:26:37 2020 - [info] Checking replication health on 192.168.68.133..
Sat Aug  8 11:26:37 2020 - [info]  ok.
Sat Aug  8 11:26:37 2020 - [info] 192.168.68.131 can be new master.
Sat Aug  8 11:26:37 2020 - [info] 
From:
192.168.68.132(192.168.68.132:3306) (current master)
 +--192.168.68.131(192.168.68.131:3306)
 +--192.168.68.133(192.168.68.133:3306)

To:
192.168.68.131(192.168.68.131:3306) (new master)
 +--192.168.68.133(192.168.68.133:3306)
 +--192.168.68.132(192.168.68.132:3306)
Sat Aug  8 11:26:37 2020 - [info] Checking whether 192.168.68.131(192.168.68.131:3306) is ok for the new master..
Sat Aug  8 11:26:37 2020 - [info]  ok.
Sat Aug  8 11:26:37 2020 - [info] 192.168.68.132(192.168.68.132:3306): SHOW SLAVE STATUS returned empty result. To check replication filtering rules, temporarily executing CHANGE MASTER to a dummy host.
Sat Aug  8 11:26:37 2020 - [info] 192.168.68.132(192.168.68.132:3306): Resetting slave pointing to the dummy host.
Sat Aug  8 11:26:37 2020 - [info] ** Phase 1: Configuration Check Phase completed.
Sat Aug  8 11:26:37 2020 - [info] 
Sat Aug  8 11:26:37 2020 - [info] * Phase 2: Rejecting updates Phase..
Sat Aug  8 11:26:37 2020 - [info] 
Sat Aug  8 11:26:37 2020 - [info] Executing master ip online change script to disable write on the current master:
Sat Aug  8 11:26:37 2020 - [info]   /usr/local/mha/scripts/master_ip_online_change --command=stop --orig_master_host=192.168.68.132 --orig_master_ip=192.168.68.132 --orig_master_port=3306 --orig_master_user=&#39;mha&#39; --new_master_host=192.168.68.131 --new_master_ip=192.168.68.131 --new_master_port=3306 --new_master_user=&#39;mha&#39; --orig_master_ssh_user=root --new_master_ssh_user=root   --orig_master_is_new_slave --orig_master_password=xxx --new_master_password=xxx
Sat Aug  8 11:26:37 2020 758267 Set read_only on the new master.. ok.
Sat Aug  8 11:26:37 2020 763087 Waiting all running 2 threads are disconnected.. (max 1500 milliseconds)
&#123;&#39;Time&#39; =&gt; &#39;1507&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;14&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.133:60218&#39;&#125;
&#123;&#39;Time&#39; =&gt; &#39;227&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;18&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.131:60292&#39;&#125;
Sat Aug  8 11:26:38 2020 267483 Waiting all running 2 threads are disconnected.. (max 1000 milliseconds)
&#123;&#39;Time&#39; =&gt; &#39;1508&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;14&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.133:60218&#39;&#125;
&#123;&#39;Time&#39; =&gt; &#39;228&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;18&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.131:60292&#39;&#125;
Sat Aug  8 11:26:38 2020 771131 Waiting all running 2 threads are disconnected.. (max 500 milliseconds)
&#123;&#39;Time&#39; =&gt; &#39;1508&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;14&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.133:60218&#39;&#125;
&#123;&#39;Time&#39; =&gt; &#39;228&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;18&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.131:60292&#39;&#125;
Sat Aug  8 11:26:39 2020 274787 Set read_only=1 on the orig master.. ok.
Sat Aug  8 11:26:39 2020 276192 Waiting all running 2 queries are disconnected.. (max 500 milliseconds)
&#123;&#39;Time&#39; =&gt; &#39;1509&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;14&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.133:60218&#39;&#125;
&#123;&#39;Time&#39; =&gt; &#39;229&#39;,&#39;db&#39; =&gt; undef,&#39;Id&#39; =&gt; &#39;18&#39;,&#39;User&#39; =&gt; &#39;repl&#39;,&#39;State&#39; =&gt; &#39;Master has sent all binlog to slave; waiting for more updates&#39;,&#39;Command&#39; =&gt; &#39;Binlog Dump GTID&#39;,&#39;Info&#39; =&gt; undef,&#39;Host&#39; =&gt; &#39;192.168.68.131:60292&#39;&#125;
Sat Aug  8 11:26:39 2020 778101 Killing all application threads..
Sat Aug  8 11:26:39 2020 778804 done.
Disabling the VIP an old master: 192.168.68.132 
Warning: Executing wildcard deletion to stay compatible with old scripts.
         Explicitly specify the prefix length (192.168.68.135/32) to avoid this warning.
         This special behaviour is likely to disappear in further releases,
         fix your scripts!
Sat Aug  8 11:26:39 2020 - [info]  ok.
Sat Aug  8 11:26:39 2020 - [info] Locking all tables on the orig master to reject updates from everybody (including root):
Sat Aug  8 11:26:39 2020 - [info] Executing FLUSH TABLES WITH READ LOCK..
Sat Aug  8 11:26:39 2020 - [info]  ok.
Sat Aug  8 11:26:39 2020 - [info] Orig master binlog:pos is MHA-LHR-Slave1-ip132-bin.000008:234.
Sat Aug  8 11:26:39 2020 - [info]  Waiting to execute all relay logs on 192.168.68.131(192.168.68.131:3306)..
Sat Aug  8 11:26:39 2020 - [info]  master_pos_wait(MHA-LHR-Slave1-ip132-bin.000008:234) completed on 192.168.68.131(192.168.68.131:3306). Executed 0 events.
Sat Aug  8 11:26:39 2020 - [info]   done.
Sat Aug  8 11:26:39 2020 - [info] Getting new master&#39;s binlog name and position..
Sat Aug  8 11:26:39 2020 - [info]  MHA-LHR-Master1-ip131-bin.000013:234
Sat Aug  8 11:26:39 2020 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST=&#39;192.168.68.131&#39;, MASTER_PORT=3306, MASTER_AUTO_POSITION=1, MASTER_USER=&#39;repl&#39;, MASTER_PASSWORD=&#39;xxx&#39;;
Sat Aug  8 11:26:39 2020 - [info] Executing master ip online change script to allow write on the new master:
Sat Aug  8 11:26:39 2020 - [info]   /usr/local/mha/scripts/master_ip_online_change --command=start --orig_master_host=192.168.68.132 --orig_master_ip=192.168.68.132 --orig_master_port=3306 --orig_master_user=&#39;mha&#39; --new_master_host=192.168.68.131 --new_master_ip=192.168.68.131 --new_master_port=3306 --new_master_user=&#39;mha&#39; --orig_master_ssh_user=root --new_master_ssh_user=root   --orig_master_is_new_slave --orig_master_password=xxx --new_master_password=xxx
Sat Aug  8 11:26:40 2020 027564 Set read_only=0 on the new master.
Enabling the VIP 192.168.68.135 on the new master: 192.168.68.131 
Sat Aug  8 11:26:42 2020 - [info]  ok.
Sat Aug  8 11:26:42 2020 - [info] 
Sat Aug  8 11:26:42 2020 - [info] * Switching slaves in parallel..
Sat Aug  8 11:26:42 2020 - [info] 
Sat Aug  8 11:26:42 2020 - [info] -- Slave switch on host 192.168.68.133(192.168.68.133:3306) started, pid: 640
Sat Aug  8 11:26:42 2020 - [info] 
Sat Aug  8 11:26:44 2020 - [info] Log messages from 192.168.68.133 ...
Sat Aug  8 11:26:44 2020 - [info] 
Sat Aug  8 11:26:42 2020 - [info]  Waiting to execute all relay logs on 192.168.68.133(192.168.68.133:3306)..
Sat Aug  8 11:26:42 2020 - [info]  master_pos_wait(MHA-LHR-Slave1-ip132-bin.000008:234) completed on 192.168.68.133(192.168.68.133:3306). Executed 0 events.
Sat Aug  8 11:26:42 2020 - [info]   done.
Sat Aug  8 11:26:42 2020 - [info]  Resetting slave 192.168.68.133(192.168.68.133:3306) and starting replication from the new master 192.168.68.131(192.168.68.131:3306)..
Sat Aug  8 11:26:42 2020 - [info]  Executed CHANGE MASTER.
Sat Aug  8 11:26:43 2020 - [info]  Slave started.
Sat Aug  8 11:26:44 2020 - [info] End of log messages from 192.168.68.133 ...
Sat Aug  8 11:26:44 2020 - [info] 
Sat Aug  8 11:26:44 2020 - [info] -- Slave switch on host 192.168.68.133(192.168.68.133:3306) succeeded.
Sat Aug  8 11:26:44 2020 - [info] Unlocking all tables on the orig master:
Sat Aug  8 11:26:44 2020 - [info] Executing UNLOCK TABLES..
Sat Aug  8 11:26:44 2020 - [info]  ok.
Sat Aug  8 11:26:44 2020 - [info] Starting orig master as a new slave..
Sat Aug  8 11:26:44 2020 - [info]  Resetting slave 192.168.68.132(192.168.68.132:3306) and starting replication from the new master 192.168.68.131(192.168.68.131:3306)..
Sat Aug  8 11:26:44 2020 - [info]  Executed CHANGE MASTER.
Sat Aug  8 11:26:44 2020 - [info]  Slave started.
Sat Aug  8 11:26:44 2020 - [info] All new slave servers switched successfully.
Sat Aug  8 11:26:44 2020 - [info] 
Sat Aug  8 11:26:44 2020 - [info] * Phase 5: New master cleanup phase..
Sat Aug  8 11:26:44 2020 - [info] 
Sat Aug  8 11:26:44 2020 - [info]  192.168.68.131: Resetting slave info succeeded.
Sat Aug  8 11:26:44 2020 - [info] Switching master to 192.168.68.131(192.168.68.131:3306) completed successfully.
3.3 测试场景二：主库故障手动转移
测试场景一测试的是，在主库故障后，MHA自动执行故障转移动作。

测试场景二测试的是，在主库故障后，MHA进程未启动的情况下，我们手动来切换。这种情况为MySQL主从关系中主库因为故障宕机了，但是MHA Master监控并没有开启，这个时候就需要手动来failover了。该情况下，日志打印输出和自动failover是没有什么区别的。需要注意的是，如果主库未宕机，那么不能手动执行故障切换，会报错的。

# 关闭主库
docker stop MHA-LHR-Master1-ip131

# 在134上执行手动切换
masterha_master_switch --conf=/etc/mha/mha.cnf --master_state=dead --ignore_last_failover --interactive=0 \
--dead_master_host=192.168.68.131 --dead_master_port=3306 \
--new_master_host=192.168.68.132 -―new_master_port=3306
接下来，宕掉的主库需要手动恢复，这里不再详细演示。需要注意的是，手动切换也会发送告警邮件。

3.4 mysql-utilities包
# 安装mysql-utilities包，依赖于Python2.7，版本需要对应，否则报错
rpm -e mysql-connector-python-2.1.8-1.el7.x86_64 --nodeps

#centos 7
rpm -Uvh http://repo.mysql.com/yum/mysql-connectors-community/el/7/x86_64/mysql-connector-python-1.1.6-1.el7.noarch.rpm
#centos 6
rpm -Uvh http://repo.mysql.com/yum/mysql-connectors-community/el/6/x86_64/mysql-connector-python-1.1.6-1.el6.noarch.rpm

yum install -y mysql-utilities
我的镜像环境已安装配置好了，直接执行即可，执行结果：

[root@MHA-LHR-Monitor-ip134 /]# mysqlrplshow --master=root:lhr@192.168.68.131:3306 --discover-slaves-login=root:lhr --verbose
# master on 192.168.68.131: ... connected.
# Finding slaves for master: 192.168.68.131:3306

# Replication Topology Graph
192.168.68.131:3306 (MASTER)
   |
   +--- 192.168.68.132:3306 [IO: Yes, SQL: Yes] - (SLAVE)
   |
   +--- 192.168.68.133:3306 [IO: Yes, SQL: Yes] - (SLAVE)

[root@MHA-LHR-Monitor-ip134 /]# mysqlrplcheck --master=root:lhr@192.168.68.131:3306 --slave=root:lhr@192.168.68.132:3306 -v   
# master on 192.168.68.131: ... connected.
# slave on 192.168.68.132: ... connected.
Test Description                                                     Status
---------------------------------------------------------------------------
Checking for binary logging on master                                [pass]
Are there binlog exceptions?                                         [WARN]

+---------+--------+--------------------------------------------------+
| server  | do_db  | ignore_db                                        |
+---------+--------+--------------------------------------------------+
| master  |        | mysql,information_schema,performance_schema,sys  |
| slave   |        | information_schema,performance_schema,mysql,sys  |
+---------+--------+--------------------------------------------------+

Replication user exists?                                             [pass]
Checking server_id values                                            [pass]

 master id = 573306131
  slave id = 573306132

Checking server_uuid values                                          [pass]

 master uuid = c8ca4f1d-aec3-11ea-942b-0242c0a84483
  slave uuid = d24a77d1-aec3-11ea-9399-0242c0a84484

Is slave connected to master?                                        [pass]
Check master information file                                        [WARN]

Cannot read master information file from a remote machine.

Checking InnoDB compatibility                                        [pass]
Checking storage engines compatibility                               [pass]
Checking lower_case_table_names settings                             [pass]

  Master lower_case_table_names: 0
   Slave lower_case_table_names: 0

Checking slave delay (seconds behind master)                         [pass]
# ...done.
</code></pre>

      </div>
      <div class="post-tags-categories">
        
      </div>
      
        <div class="copyright">
  <ul class="post-copyright">
    <li class="post-copyright-author">
    <strong>作者:  </strong>张存</a>
    </li>
    <li class="post-copyright-link">
    <strong>文章链接:  </strong>
    <a href="/2022/09/26/zai-docker-zhong-shi-yong-mysql-gao-ke-yong-zhi-mha/" target="_blank" title="在Docker中使用MySQL高可用之MHA">https://blog.zhangcun.store/2022/09/26/zai-docker-zhong-shi-yong-mysql-gao-ke-yong-zhi-mha/</a>
    </li>
    <li class="post-copyright-license">
      <strong>版权声明:   </strong>
      本网站所有文章除特别声明外,均采用 <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">CC BY-NC-ND 4.0</a>
      许可协议。转载请注明出处!
    </li>
  </ul>
<div>
      
    </article>
    <!-- 上一篇文章和下一篇文章 -->
    
      <!-- 文章详情页的上一页和下一页 -->
<div class="post-nav">



  
  <div class="post-nav-prev post-nav-item">
    <div class="post-nav-img lazyload" data-original="/medias/14.jpg" style="background-size: cover; 
      background-position: center center;">
    </div>
    <a href="/2022/09/26/kubeadm-an-zhuang-kubernetes-shi-jian-ji-lu/" class="post-nav-link">
      <div class="title">
        <i class="fa fa-angle-left"></i> 上一篇:
        <div class="title-text">kubeadm安装Kubernetes，实践记录</div>
      </div>
      
      <!-- <div class="content">
        kubeadm安装Kubernetes，实践记录
这是一次成功的安装、胜利的安装，是值得收藏的一次记录。

本文主要介绍
      </div> -->
    </a>
  </div>



  
  <div class="post-nav-next post-nav-item">
    <div class="post-nav-img lazyload" data-original="/medias/9.jpg" style="background-size: cover; 
      background-position: center center;">
    </div>
    <a href="/2022/09/22/linux-she-zhi-ntp-shi-jian-tong-bu/" class="post-nav-link">
      <div class="title">
        下一篇: <i class="fa fa-angle-right"></i>
        <div class="title-text">linux设置NTP时间同步</div>
      </div>
      <!-- <div class="content">
        说明
设置时间同步保证记录日志的时间的准确性。
检查方法
1)在终端中输入命令：
[test@localhost ~]$
      </div> -->
    </a>
  </div>

</div>

    
    

    <!-- 打赏 -->
    
      <div id="appDonate" class="post-donate">
  <div id="donate_board" class="donate_bar center" ref="donate">
    <a id="btn_donate" class="btn_donate" href="javascript:;" title="打赏" @click="showDialogDrawer()"></a>
  </div>
  <transition name="fade">
    <div 
      class="donate-box-mask"
      v-cloak 
      v-if="visible"
      @click="cancelDialogDrawer()"
    >
    </div>
  </transition>
  <transition name="bounce">
    <div class="donate-box" v-cloak v-if="visible">
      <div class="donate-box_close">
        <i class="fa fa-times" aria-hidden="true" @click="cancelDialogDrawer" pointer></i>
      </div>
      <div class="donate-box_title">
        <h4>
          你的赏识是我前进的动力
        </h4>
      </div>
      <div class="donate-box_tab">
        <div class="Alipay" pointer :class="{'active': tabActive === 'Alipay'}" @click="changeTabActive('Alipay')">
          支付宝
        </div>
        <div class="WeChatpay" pointer :class="{'active': tabActive === 'WeChatpay'}" @click="changeTabActive('WeChatpay')">
          微信
        </div>
      </div>
      <div class="donate-box_img">
        <div class="AlipayImg" v-if="tabActive === 'Alipay'">
          <img src="/medias/zfb.jpg" alt="支付宝打赏" />
        </div> 
        <div class="WeChatpayImg" v-if="tabActive === 'WeChatpay'">
          <img src="/medias/wx.jpg" alt="微信打赏" />
        </div>
      </div>
    </div>
  </transition>
</div>

<script>
  var body = document.body || document.documentElement || window;
  var vm = new Vue({
    el: '#appDonate',
    data: {
      visible: false,
      tabActive: 'Alipay',
      top: 0,
    },
    computed: {
    },
    mounted() {
    },
    methods: {
      showDialogDrawer() {
        this.visible = true;
        // 防止页面滚动，只能让弹框滚动
        // function getScroll() {
        //   return {
        //     left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0,
        //     top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
        //   };
        // }
        this.top = $(document).scrollTop() // or getScroll().top
        // console.log('aa', $('.main-content'));
        body.style.cssText = 'overflow: hidden;';
      },
      cancelDialogDrawer() {
        this.visible = false;
        body.removeAttribute('style');
        $(document).scrollTop(this.top)
      },
      changeTabActive(name) {
        this.tabActive = name;
      }
    },
    created() {}
  })
</script>
    

    <!-- 分享 -->
    
      <!-- https://github.com/overtrue/share.js -->
<!-- 文章详情页的分享 -->
<div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>

<script src="/js/shareJs/social-share.min.js"></script>
</script>

<style>
  .social-share {
    margin: 20px 0;
  }
</style>


    
    
    <!-- 评论 -->
    <!-- 评论 -->

  <div id="myComment">
    
      <div id="gitment-container"></div>

    
  </div>

<!-- comment script in themes\hexo-theme-bamboo\layout\_partial\scripts\index.ejs -->


  </div>

  <!-- 目录 -->
  <!-- 文章详情页右侧目录 -->

  <div class="toc-aside">
    <div class="toc-main">
      <div class="toc-aside-title">
        <i class="fa fa-list-ul" aria-hidden="true"></i><span>本文目录</span>
        
          <div class="toc-open-close">本文目录</div>
        
      </div>
      <div class="toc-content">
        <div class="toc"></div>
      </div>
    </div>
  </div>
  <!-- js在scripts目录下的的toc.ejs里面 -->
  

  <script>
    function openBtnClickFn () {
      let openOrCloseBtn = $('.toc-aside .toc-aside-title .toc-open-close');
      let open = eval('' || 'true');
      openOrCloseBtn.click(function() {
        if (open === true) {
          $(".toc-aside").css({'width': 0, 'padding': 0});
          $(".toc-content").css({'width': 0});
          $(".toc-aside-title span, .toc-aside-title i").css({'display': 'none'});
          $(".main-content").css({'width': '75%', 'margin': '10px auto'});
          open = false;
        } else {
          $(".toc-aside").css({'width': '300px', 'padding': '0 10px'});
          $(".toc-content").css({'width': '300px'});
          $(".toc-aside-title span, .toc-aside-title i").css({'display': 'inline-block'});
          $(".main-content").css({'width': '65%', 'margin-right': '10px', 'margin-left': 'calc(35% - 350px)'});
          open = true;
        }
      });
    };
    openBtnClickFn();
    document.addEventListener('pjax:complete', function () {
      openBtnClickFn();
    })
  </script>


  

  <!-- 图片放大 Wrap images with fancybox support -->
  <script src="/js/wrapImage.js"></script>
</div>

<!-- 文章详情页背景图 -->
<div id="appBgSwiper" style="position: fixed;left: 0;top: 0;width: 100%;height: 100%;z-index: -2;" :style="{'background-color': bgColor ? bgColor : 'transparent'}">
    <transition-group tag="ul" :name="names">
        <li v-for='(image,index) in img' :key='index' v-show="index === mark" class="bg-swiper-box">
            <img :src="image" class="bg-swiper-img">
        </li>
    </transition-group>
</div>
<script>
    var vm = new Vue({
        el: '#appBgSwiper',
        data: {
            names: '' || 'fade' || 'fade', // translate-fade fade
            mark: 0,
            img: [],
            bgColor: '',
            time: null
        },
        methods: {   //添加方法
            change(i, m) {
                if (i > m) {
                    // this.names = 'fade';
                } else if (i < m) {
                    // this.names = 'fade';
                } else {
                    return;
                }
                this.mark = i;
            },
            prev() {
                // this.names = 'fade';
                this.mark--;
                if (this.mark === -1) {
                    this.mark = 3;
                    return
                }
            },
            next() {
                // this.names = 'fade';
                this.mark++;
                if (this.mark === this.img.length) {
                    this.mark = 0;
                    return
                }
            },
            autoPlay() {
                // this.names = 'fade';
                this.mark++;
                if (this.mark === this.img.length) {
                    this.mark = 0;
                    return
                }
            },
            play() {
                let bgImgDelay = '' || '180000'
                let delay = parseInt(bgImgDelay) || 180000;
                this.time = setInterval(this.autoPlay, delay);
            },
            enter() {
                clearInterval(this.time);
            },
            leave() {
                this.play();
            }
        },
        created() {
            this.play()
        },
        beforeDestroy() {
            clearInterval(this.time);
        },
        mounted() {
            let prop = ''|| '';
            let isImg = prop.includes('.bmp') || prop.includes('.jpg') || prop.includes('.png') || prop.includes('.tif') || prop.includes('.gif') || prop.includes('.pcx') || prop.includes('.tga') || prop.includes('.exif') || prop.includes('.fpx') || prop.includes('.psd') || prop.includes('.cdr') || prop.includes('.pcd') || prop.includes('.dxf') || prop.includes('.ufo') || prop.includes('.eps') || prop.includes('.ai') || prop.includes('.raw') || prop.includes('.WMF') || prop.includes('.webp') || prop.includes('.jpeg') || prop.includes('http://') || prop.includes('https://') 
            if (isImg) {
                let img = prop.split(',');
                let configRoot = '/'
                let arrImg = [];
                img.forEach(el => {
                    var Expression=/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
			        var objExp = new RegExp(Expression);

                    if(objExp.test(el)) {
                        // http or https
                        arrImg.push(el);
                    } else {
                        // 非http or https开头
                        // 本地文件
                        let firstStr = el.charAt(0);
                        if (firstStr == '/') {
                            el = el.substr(1); // 删除第一个字符 '/',因为 configRoot最后一个字符为 /
                        }
                        el = configRoot + el;
                        arrImg.push(el);
                    }
                })
                this.img = arrImg;
            } else {
                this.bgColor = prop;
            }
        }
    })
</script>

<style>
    .bg-swiper-box {
        position: absolute;
        display: block;
        width: 100%;
        height: 100%;
    }
    .bg-swiper-img {
        object-fit: cover;
        width: 100%;
        height: 100%;
    }
</style>

      </main>
    </div>

    <!-- 页脚 -->
    
  
  <div class="footer bg-color">
    <div class="footer-main">
      
        
          <div class="link">
            
              
                <a href="/atom.xml" class="social">
                  
                    <i class="fa fa-rss" aria-hidden="true"></i>
                  
                </a>
              
            
              
                <a href="mailto:1776803208@qq.com" class="social">
                  
                    <i class="fa fa-envelope" aria-hidden="true"></i>
                  
                </a>
              
            
              
                <a target="_blank" rel="noopener" href="https://github.com/zhangcun1113/hexo-theme-bamboo" class="social">
                  
                    <i class="fa fa-github" aria-hidden="true"></i>
                  
                </a>
              
            
              
                <a href="tencent://AddContact/?fromId=50&amp;fromSubId=1&amp;subcmd=all&amp;uin=1730241541" class="social">
                  
                    <i class="fa fa-qq" aria-hidden="true"></i>
                  
                </a>
              
            
          </div>
        
      
        
          <div class="footer-copyright">
            <p>Copyright © 2001 - 2023 <a href="https://blog.zhangcun.store">zhangcun</a> </p>

          </div>
        
      
        
          
            <!-- 不蒜子统计 -->
            <!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
      <i class="fa fa-eye" aria-hidden="true"></i>本站总访问量：<span id="busuanzi_value_site_pv"></span> 次
</span>
<span class="post-meta-divider">|</span>
<span id="busuanzi_container_site_uv" style='display:none'>
      <i class="fa fa-users" aria-hidden="true"></i>本站访客数：<span id="busuanzi_value_site_uv"></span> 人
</span>

          
        
      
        
          <div class="footer-custom">
            
          </div>
        
      
    </div>
  </div>



    <!-- 渲染暗黑按钮 -->
    
      <div class="dark">
  <div class="dark-content">
    <i class="fa fa-moon-o" aria-hidden="true"></i>
    <!-- <span>关灯</span> -->
  </div>
  
</div>

<script>
  $(function() {
    let isDark = JSON.parse(localStorage.getItem('dark'))  || JSON.parse('false');
    if (isDark) {
      $(".dark-content").replaceWith(
          `
          <div class='dark-content'>
            <i class="fa fa-lightbulb-o" aria-hidden="true"></i>
          </div>
          `
        );
    }
    $('.dark').click(function() {
      if ($(document.body).is('.darkModel')) {
        $(document.body).removeClass('darkModel');
        localStorage.setItem('dark', false);
        $(".dark-content").replaceWith(
          `
          <div class='dark-content'>
            <i class="fa fa-moon-o" aria-hidden="true"></i>
          </div>
          `
        );
      } else {
        $(document.body).addClass('darkModel');
        localStorage.setItem('dark', true);
        $(".dark-content").replaceWith(
          `
          <div class='dark-content'>
            <i class="fa fa-lightbulb-o" aria-hidden="true"></i>
          </div>
          `
        );
      }
    })
  })
</script>
    
    <!-- 渲染回到顶部按钮 -->
    
      <div class="goTop top-btn-color" pointer>
  <i class="fa fa-arrow-up" aria-hidden="true"></i>
</div>
<script src="/js/goTop.js"></script>
    
    <!-- 渲染左下角音乐播放器 -->
    
      <link rel="stylesheet" href="/js/aplayer/APlayer@1.10.1.min.css">
<style>
.aplayer .aplayer-lrc p {
  
  font-size: 12px;
  font-weight: 700;
  line-height: 16px !important;
}

.aplayer .aplayer-lrc p.aplayer-lrc-current {
  
  font-size: 15px;
  color: #22b1eb;
}


.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
  left: -66px !important;
}

.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
  left: 0px !important;
}


</style>
<meting-js  
  class=""
  server="netease"
  type="playlist"
  id="8444991407"
  fixed='true'
  autoplay='true'
  theme='#22b1eb'
  loop='all'
  order='random'
  preload='auto'
  volume='0.7'
  list-folded='true'
>
</meting-js>

<!-- <style>
  #aplayer {
    position: fixed;
    left: 0;
    bottom: 300px;
  }
</style> -->
<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/meting@2/dist/Meting.min.js"></script>
    

    <!-- 图片放大 -->
    
      <script src="/js/fancybox/jquery.fancybox.min.js"></script>
    

    <!-- 百度解析 -->
    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <!-- 背景彩带 -->
    
    
    <script src="/js/app.js" data-pjax></script>
    
    <!-- 文章目录所需js -->
<link href="/js/tocbot/tocbot.css" rel="stylesheet">
<script src="/js/tocbot/tocbot.min.js"></script>

<script>
  var headerEl = 'h2, h3, h4',  //headers 
      content = '.post-detail',//文章容器
      idArr = {};  //标题数组以确定是否增加索引id
      //add #id
  var option = {
    // Where to render the table of contents.
    tocSelector: '.toc',
    // Where to grab the headings to build the table of contents.
    contentSelector: content,
    // Which headings to grab inside of the contentSelector element.
    headingSelector: headerEl,
    scrollSmooth: true,
    scrollSmoothOffset: -80,
    headingsOffset: -($(window).height() * 0.4 - 45),
    positionFixedSelector: '.toc-main',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto',
  }
  if ($('.toc').length > 0) {

    $(content).children(headerEl).each(function () {
      //去除空格以及多余标点
      var headerId = $(this).text().replace(/[\s|\~|`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\||\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?|\：|\，|\。]/g, '');

      headerId = headerId.toLowerCase();
      if (idArr[headerId]) {
        //id已经存在
        $(this).attr('id', headerId + '-' + idArr[headerId]);
        idArr[headerId]++;
      }
      else {
        //id未存在
        idArr[headerId] = 1;
        $(this).attr('id', headerId);
      }
    });

    tocbot.init(option);
    
  }


  document.addEventListener('pjax:send', function () {
    if ($('.toc').length) {
      tocbot.destroy();
    }
  });

  document.addEventListener('pjax:complete', function () {
    if ($('.toc').length) {
      tocbot.init(option);
    }
  });

  
</script>

<style>
.is-position-fixed {
  position:sticky !important;
  top: 74px;
}
.toc-main ul {
  counter-reset: show-list;
}
.toc-main ul li::before {
  content: counter(item)".";
  display: block;
  position: absolute;
  left: 12px;
  top:0;
}
/* .toc > .toc-list {
  padding-left: 35px;
}
.toc>.toc-list li {
  list-style: decimal;
}
.toc-list {
  padding-left: 25px;
} */
</style> 

<!-- 渲染issues标签里的内容 -->
<script>
  function loadIssuesJS() {
    if ($(".post-detail").find(".issues-api").length == 0) {
      return;
    } 
    loadScript('/js/issues/index.js');
  };
  $(function () {
    loadIssuesJS();
  });
  document.addEventListener('pjax:complete', function () {
    if (typeof IssuesAPI == "undefined") {
      loadIssuesJS();
    }
  })
</script>

<!-- 输入框打字特效 -->
<!-- 输入框打字特效 -->

  <script src="/js/activate-power-mode.js"></script>
  <script>
    POWERMODE.colorful = true;  // 打开随机颜色特效
    POWERMODE.shake = false;    // 关闭输入框抖动
    document.body.addEventListener('input', POWERMODE);//监听打字事件
  </script>


<!-- markdown代码一键复制功能 -->

  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/v-plugs-ayu/lib/ayu.css">
  <script src="https://cdn.jsdelivr.net/npm/v-plugs-ayu/lib/ayu.umd.min.js"></script>
  <script src="/js/clipboard/clipboard.min.js"></script>
  <div id="appCopy">
  </div>
  <script data-pjax>
    var vm = new Vue({
      el: '#appCopy',
      data: {
      },
      computed: {
      },
      mounted() {
        const that = this;
        var copy = '复制';
        /* code */
        var initCopyCode = function () {
          var copyHtml = '';
          copyHtml += '<button class="btn-copy" data-clipboard-snippet="" style="position:absolute;top:0;right:0;z-index:1;">';
          copyHtml += '<i class="fa fa-copy"></i><span>' + copy + '</span>';
          copyHtml += '</button>';
          $(".post-detail pre").not('.gutter pre').wrap("<div class='codeBox' style='position:relative;width:100%;'></div>")
          $(".post-detail pre").not('.gutter pre').before(copyHtml);
          new ClipboardJS('.btn-copy', {
            target: function (trigger) {
              return trigger.nextElementSibling;
            }
          });
        }
        initCopyCode();
        $('.btn-copy').unbind('click').bind('click', function () {
          doSomething();
        })
        $(document).unbind('keypress').bind('keypress', function (e) {
          if (e.ctrlKey && e.keyCode == 67) {
            doSomething();
            // 返回false, 防止重复触发copy事件  
            // return false;
          }
        })
        //  监听 ctrl + c事件  
        // $(document).unbind('keypress').bind('keypress', function (e) {
        //   if (e.ctrlKey && e.keyCode == 67) {
        //     doSomething();
        //     // 返回false, 防止重复触发copy事件  
        //     return false;
        //   }
        // })

        // // 鼠标右键的复制事件  
        // $(document).unbind('copy').bind('copy', function (e) {
        //   doSomething();
        // });

        function doSomething() {
          that.$notify({
            title: "成功",
            content: "代码已复制，请遵守相关授权协议。",
            type: 'success'
          })
        }
      },
      methods: {
      },
      created() { }
    })
  </script>


<!-- 图片懒加载 -->
<script src="/js/lazyload/lazyload@1.9.1.js"></script>
<script data-pjax>
  $(function () {
    $("div.lazyload, img.lazyload, a.lazyload").lazyload(
      {
        effect: "show",
        placeholder: "https://img10.360buyimg.com/ddimg/jfs/t1/159345/8/2271/222193/5ff7b36fEe49f5663/a95287c20385127f.gif"    //图片未加载出来时显示的占位图
      }
    );
  });
</script>

<!-- 卡片滚动动画 -->
   

<!-- 评论所需js -->

    
        <script type="text/javascript">
    var bamboo = {};
    bamboo.utterance = {};
    
    function check_utterance() {
        let isDark = JSON.parse(localStorage.getItem('dark'))  || JSON.parse('false');
        if (isDark) {
            bamboo.utterance.Theme = 'github-dark';
        } else {
            bamboo.utterance.Theme = 'github-light';
        }

        return document.getElementById("gitment-container");
    }
    comment_el = '#gitment-container';
    load_utterance = function () {
        if ($(comment_el).length) {
            // 匿名函数，防止污染全局变量
            const HEAD = check_utterance();
            
            var utterances = document.createElement('script');
            utterances.type = 'text/javascript';
            utterances.async = true;
            utterances.setAttribute('issue-term','pathname')
            utterances.setAttribute('theme', bamboo.utterance.Theme)
            utterances.setAttribute('repo','')
            utterances.crossorigin = 'anonymous';
            utterances.src = 'https://utteranc.es/client.js';
            // content 是要插入评论的地方
            document.getElementById('gitment-container').appendChild(utterances);
            
        }
    }

    function dark_utterance() {
        const HEAD = check_utterance();
        if (!HEAD) return;
        const message = {
            type: 'set-theme',
            theme: bamboo.utterance.Theme
        };
        const utteranceIframe = document.querySelector('iframe');
        utteranceIframe.contentWindow.postMessage(message, 'https://utteranc.es');
    }

    $(document).ready(load_utterance);
    document.addEventListener('pjax:complete', function () {
        load_utterance();
    });

    $('.dark').click(function() {
        setTimeout(() => {
            dark_utterance();
        });
    })
    
</script>

<style>
    .utterances {
        max-width: inherit!important;
    }
</style>
    


<!-- 鼠标点击特效 -->
<!-- 爱心点击 -->

	
		<canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 999; pointer-events: none;" ></canvas>
		<script src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script>
		<script src="/js/cursor/explosion.min.js"></script>
	




    <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" data-pjax></script>


    <!-- pjax -->
    

<!-- pjax -->


  <script src="/js/pjax@0.2.8/index.js"></script>
  
    <!-- 样式位于：source/css/_third-party/pjaxanimate.styl -->

<div class="pjax-animate">
  
    <div class="loading-circle"><div id="loader-circle"></div></div>
    <script>
      window.ShowLoading = function() {
        $(".loading-circle").css("display", "block");
      };
      window.HideLoading = function() {
        $(".loading-circle").css("display", "none");
      }
    </script>
  
	<script>
    document.addEventListener('pjax:complete', function () {
      window.HideLoading();
    })
    document.addEventListener('pjax:send', function () {
      window.ShowLoading();
    })
    document.addEventListener('pjax:error', function () {
      window.HideLoading();
    })
	</script>
</div>

  

  <script>
    var pjax = new Pjax({
      elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([no-pjax])',   // 拦截正常带链接的 a 标签
      selectors: ["#pjax-container","title"],                                   // 根据实际需要确认重载区域
      cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
      timeout: 5000
    });

    document.addEventListener('pjax:send', function (e) {

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      $(window).unbind('resize');
      $(window).unbind('scroll');
      $(document).unbind('scroll');
      $(document).unbind('click');
      $('body').unbind('click');

    })
    
    document.addEventListener('pjax:complete', function () {
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });
    });

    document.addEventListener('pjax:error', function (e) {
      window.location.href = e.triggerElement.href;
    })
    
    // 刷新不从顶部开始
    document.addEventListener("DOMContentLoaded", function () {
      history.scrollRestoration = 'auto';
    })
  </script>



  </body>
</html>